diff --git a/CHANGES_NEXT_RELEASE b/CHANGES_NEXT_RELEASE index f688928f..a0a3d7dc 100644 --- a/CHANGES_NEXT_RELEASE +++ b/CHANGES_NEXT_RELEASE @@ -1,3 +1,4 @@ +- Fix: allow send multiple measures in MQTT to CB in a batch (POST /v2/op/update) and sorted by TimeInstant when possible, instead of using multiples single request (#825, iotagent-node-lib#1612) (reopened) - Add: allow MQTT single array measures (#856) - Fix: check endpoint expression when execute http command - Fix: use config.defaultTransport (from config.js or IOTA_DEFAULT_TRANSPORT env var) instead of magic 'HTTP' at provision device diff --git a/lib/commonBindings.js b/lib/commonBindings.js index 0410f55f..26fd68bc 100644 --- a/lib/commonBindings.js +++ b/lib/commonBindings.js @@ -283,50 +283,46 @@ function singleMeasure(apiKey, deviceId, attribute, device, parsedMessage) { */ function multipleMeasures(apiKey, deviceId, device, messageObj) { let measure; + let values; const ctxt = fillService(context, device); config.getLogger().debug(context, 'Processing multiple measures for device %s with apiKey %s', deviceId, apiKey); + let attributesArray = []; for (let j = 0; j < messageObj.length; j++) { measure = messageObj[j]; - let attributesArray = []; - const values = extractAttributes(device, measure, device.payloadType); + values = extractAttributes(device, measure, device.payloadType); if (values && values[0] && values[0][0]) { - // multi measure is extracted due to payloadType is ngsi - attributesArray = values; + // Check multimeasure from a ngsiv2/ngsild entities array + attributesArray = attributesArray.concat(values); } else { - attributesArray = [values]; + attributesArray.push(values); } - config - .getLogger() - .debug( - context, - 'Processing multiple measures for device %s with apiKey %s values %j', - deviceId, - apiKey, - attributesArray - ); - iotAgentLib.update(device.name, device.type, '', attributesArray, device, function (error) { - if (error) { - config.getLogger().error( - ctxt, - /*jshint quotmark: double */ - "MEASURES-002: Couldn't send the updated values to the Context Broker due to an error: %j", - /*jshint quotmark: single */ - error - ); - } else { - config - .getLogger() - .info( - ctxt, - 'Multiple measures for device %s with apiKey %s successfully updated', - deviceId, - apiKey - ); - } - finishSouthBoundTransaction(null); - }); } + config + .getLogger() + .debug( + context, + 'Processing multiple measures for device %s with apiKey %s values %j', + deviceId, + apiKey, + attributesArray + ); + iotAgentLib.update(device.name, device.type, '', attributesArray, device, function (error) { + if (error) { + config.getLogger().error( + ctxt, + /*jshint quotmark: double */ + "MEASURES-002: Couldn't send the updated values to the Context Broker due to an error: %j", + /*jshint quotmark: single */ + error + ); + } else { + config + .getLogger() + .info(ctxt, 'Multiple measures for device %s with apiKey %s successfully updated', deviceId, apiKey); + } + finishSouthBoundTransaction(null); + }); } /** diff --git a/test/unit/ngsiv2/MQTT_receive_measures-test2.js b/test/unit/ngsiv2/MQTT_receive_measures-test2.js index d362646c..bfdaaf25 100644 --- a/test/unit/ngsiv2/MQTT_receive_measures-test2.js +++ b/test/unit/ngsiv2/MQTT_receive_measures-test2.js @@ -110,17 +110,8 @@ describe('MQTT: Measure reception ', function () { .matchHeader('fiware-service', 'smartgondor') .matchHeader('fiware-servicepath', '/gardens') .post( - '/v2/entities?options=upsert', - utils.readExampleFile('./test/unit/ngsiv2/contextRequests/multipleMeasuresJsonTypes.json') - ) - .reply(204); - - contextBrokerMock - .matchHeader('fiware-service', 'smartgondor') - .matchHeader('fiware-servicepath', '/gardens') - .post( - '/v2/entities?options=upsert', - utils.readExampleFile('./test/unit/ngsiv2/contextRequests/multipleMeasuresJsonTypes2.json') + '/v2/op/update', + utils.readExampleFile('./test/unit/ngsiv2/contextRequests/multipleMeasuresJsonTypes4.json') ) .reply(204); }); @@ -207,20 +198,10 @@ describe('MQTT: Measure reception ', function () { .matchHeader('fiware-service', 'smartgondor') .matchHeader('fiware-servicepath', '/gardens') .post( - '/v2/entities?options=upsert', - utils.readExampleFile('./test/unit/ngsiv2/contextRequests/unprovisionedDevice.json') + '/v2/op/update', + utils.readExampleFile('./test/unit/ngsiv2/contextRequests/unprovisionedDevice4.json') ) .reply(204); - - contextBrokerUnprovMock - .matchHeader('fiware-service', 'smartgondor') - .matchHeader('fiware-servicepath', '/gardens') - .post( - '/v2/entities?options=upsert', - utils.readExampleFile('./test/unit/ngsiv2/contextRequests/unprovisionedDevice2.json') - ) - .reply(204); - request(groupCreation, function (error, response, body) { done(); }); @@ -278,17 +259,8 @@ describe('MQTT: Measure reception ', function () { .matchHeader('fiware-service', 'smartgondor') .matchHeader('fiware-servicepath', '/gardens') .post( - '/v2/entities?options=upsert', - utils.readExampleFile('./test/unit/ngsiv2/contextRequests/unknownMeasures.json') - ) - .reply(204); - - contextBrokerMock - .matchHeader('fiware-service', 'smartgondor') - .matchHeader('fiware-servicepath', '/gardens') - .post( - '/v2/entities?options=upsert', - utils.readExampleFile('./test/unit/ngsiv2/contextRequests/unknownMeasures2.json') + '/v2/op/update', + utils.readExampleFile('./test/unit/ngsiv2/contextRequests/unknownMeasures3.json') ) .reply(204); }); @@ -338,17 +310,8 @@ describe('MQTT: Measure reception ', function () { .matchHeader('fiware-service', 'smartgondor') .matchHeader('fiware-servicepath', '/gardens') .post( - '/v2/entities?options=upsert', - utils.readExampleFile('./test/unit/ngsiv2/contextRequests/timestampMeasure.json') - ) - .reply(204); - - contextBrokerMock - .matchHeader('fiware-service', 'smartgondor') - .matchHeader('fiware-servicepath', '/gardens') - .post( - '/v2/entities?options=upsert', - utils.readExampleFile('./test/unit/ngsiv2/contextRequests/timestampMeasure2.json') + '/v2/op/update', + utils.readExampleFile('./test/unit/ngsiv2/contextRequests/timestampMeasure3.json') ) .reply(204); }); diff --git a/test/unit/ngsiv2/MQTT_receive_ngsild_measures-test.js b/test/unit/ngsiv2/MQTT_receive_ngsild_measures-test.js index db17d197..4b841e9b 100644 --- a/test/unit/ngsiv2/MQTT_receive_ngsild_measures-test.js +++ b/test/unit/ngsiv2/MQTT_receive_ngsild_measures-test.js @@ -77,7 +77,6 @@ describe('MQTT: NGSILD Measure reception ', function () { async.series([iotAgentLib.clearAll, iotaJson.stop], done); }); - describe('When a publish single NGSILD entity measure with NGSILD format arrives for the HTTP binding and NGSILD is the expected payload type', function () { const measure = { id: 'urn:ngsi-ld:ParkingSpot:santander:daoiz_velarde_1_5:3', @@ -206,16 +205,8 @@ describe('MQTT: NGSILD Measure reception ', function () { .matchHeader('fiware-service', 'smartgondor') .matchHeader('fiware-servicepath', '/gardens') .post( - '/v2/entities?options=upsert', - utils.readExampleFile('./test/unit/ngsiv2/contextRequests/ngsildPayloadMeasure.json') - ) - .reply(204); - contextBrokerMock - .matchHeader('fiware-service', 'smartgondor') - .matchHeader('fiware-servicepath', '/gardens') - .post( - '/v2/entities?options=upsert', - utils.readExampleFile('./test/unit/ngsiv2/contextRequests/ngsildPayloadMeasure2.json') + '/v2/op/update', + utils.readExampleFile('./test/unit/ngsiv2/contextRequests/ngsildPayloadMeasure3.json') ) .reply(204); }); diff --git a/test/unit/ngsiv2/MQTT_receive_ngsiv2_measures-test.js b/test/unit/ngsiv2/MQTT_receive_ngsiv2_measures-test.js index 9ee5ce95..af49ff4d 100644 --- a/test/unit/ngsiv2/MQTT_receive_ngsiv2_measures-test.js +++ b/test/unit/ngsiv2/MQTT_receive_ngsiv2_measures-test.js @@ -464,16 +464,8 @@ describe('MQTT: NGSIv2 Measure reception ', function () { .matchHeader('fiware-service', 'smartgondor') .matchHeader('fiware-servicepath', '/gardens') .post( - '/v2/entities?options=upsert', - utils.readExampleFile('./test/unit/ngsiv2/contextRequests/ngsiv2PayloadMeasure.json') - ) - .reply(204); - contextBrokerMock - .matchHeader('fiware-service', 'smartgondor') - .matchHeader('fiware-servicepath', '/gardens') - .post( - '/v2/entities?options=upsert', - utils.readExampleFile('./test/unit/ngsiv2/contextRequests/ngsiv2PayloadMeasure2.json') + '/v2/op/update', + utils.readExampleFile('./test/unit/ngsiv2/contextRequests/ngsiv2PayloadMeasure3.json') ) .reply(204); }); diff --git a/test/unit/ngsiv2/amqpBinding-test2.js b/test/unit/ngsiv2/amqpBinding-test2.js index 5bbdec12..683f0985 100644 --- a/test/unit/ngsiv2/amqpBinding-test2.js +++ b/test/unit/ngsiv2/amqpBinding-test2.js @@ -100,19 +100,10 @@ describe('AMQP Transport binding: multiple measures', function () { .matchHeader('fiware-service', 'smartgondor') .matchHeader('fiware-servicepath', '/gardens') .post( - '/v2/entities?options=upsert', - utils.readExampleFile('./test/unit/ngsiv2/contextRequests/singleMeasureAMQP3.json') + '/v2/op/update', + utils.readExampleFile('./test/unit/ngsiv2/contextRequests/multipleMeasureAMQP4.json') ) - .reply(200, utils.readExampleFile('./test/contextResponses/singleMeasureSuccess.json')); - - contextBrokerMock - .matchHeader('fiware-service', 'smartgondor') - .matchHeader('fiware-servicepath', '/gardens') - .post( - '/v2/entities?options=upsert', - utils.readExampleFile('./test/unit/ngsiv2/contextRequests/singleMeasureAMQP2.json') - ) - .reply(200, utils.readExampleFile('./test/contextResponses/singleMeasureSuccess.json')); + .reply(204); }); it('should send a single update context request with all the attributes', function (done) { @@ -135,17 +126,8 @@ describe('AMQP Transport binding: multiple measures', function () { .matchHeader('fiware-service', 'smartgondor') .matchHeader('fiware-servicepath', '/gardens') .post( - '/v2/entities?options=upsert', - utils.readExampleFile('./test/unit/ngsiv2/contextRequests/multipleMeasure.json') - ) - .reply(204); - - contextBrokerMock - .matchHeader('fiware-service', 'smartgondor') - .matchHeader('fiware-servicepath', '/gardens') - .post( - '/v2/entities?options=upsert', - utils.readExampleFile('./test/unit/ngsiv2/contextRequests/multipleMeasure2.json') + '/v2/op/update', + utils.readExampleFile('./test/unit/ngsiv2/contextRequests/multipleMeasure3.json') ) .reply(204); }); diff --git a/test/unit/ngsiv2/contextRequests/multipleMeasure3.json b/test/unit/ngsiv2/contextRequests/multipleMeasure3.json new file mode 100644 index 00000000..07a543c4 --- /dev/null +++ b/test/unit/ngsiv2/contextRequests/multipleMeasure3.json @@ -0,0 +1,29 @@ +{ + "actionType": "append", + "entities": [ + { + "id": "Second MQTT Device", + "type": "AnMQTTDevice", + "a": { + "type": "celsius", + "value": "23" + }, + "b": { + "type": "degrees", + "value": "98" + } + }, + { + "id": "Second MQTT Device", + "type": "AnMQTTDevice", + "a": { + "type": "celsius", + "value": "25" + }, + "b": { + "type": "degrees", + "value": "100" + } + } + ] + } diff --git a/test/unit/ngsiv2/contextRequests/multipleMeasureAMQP4.json b/test/unit/ngsiv2/contextRequests/multipleMeasureAMQP4.json new file mode 100644 index 00000000..bd18ba2a --- /dev/null +++ b/test/unit/ngsiv2/contextRequests/multipleMeasureAMQP4.json @@ -0,0 +1,21 @@ +{ + "actionType": "append", + "entities": [ + { + "id": "Second MQTT Device", + "type": "AnMQTTDevice", + "a": { + "type": "celsius", + "value": "23" + } + }, + { + "id": "Second MQTT Device", + "type": "AnMQTTDevice", + "a": { + "type": "celsius", + "value": "25" + } + } + ] + } diff --git a/test/unit/ngsiv2/contextRequests/multipleMeasuresJsonTypes4.json b/test/unit/ngsiv2/contextRequests/multipleMeasuresJsonTypes4.json new file mode 100644 index 00000000..bc7bd8e1 --- /dev/null +++ b/test/unit/ngsiv2/contextRequests/multipleMeasuresJsonTypes4.json @@ -0,0 +1,85 @@ +{ + "actionType": "append", + "entities": [ + { + "id": "Second MQTT Device", + "type": "AnMQTTDevice", + "temperature": { + "type": "celsius", + "value": "87" + }, + "humidity": { + "type": "degrees", + "value": "32" + }, + "luminosity": { + "type": "Integer", + "value": 10 + }, + "pollution": { + "type": "Float", + "value": 43.4 + }, + "configuration": { + "type": "Object", + "value": { + "firmware": { + "version": "1.1.0", + "hash": "cf23df2207d99a74fbe169e3eba035e633b65d94" + } + } + }, + "tags": { + "type": "Array", + "value": [ + "iot", + "device" + ] + }, + "enabled": { + "type": "Boolean", + "value": true + } + }, + { + "id": "Second MQTT Device", + "type": "AnMQTTDevice", + "temperature": { + "type": "celsius", + "value": "89" + }, + "humidity": { + "type": "degrees", + "value": "33" + }, + "luminosity": { + "type": "Integer", + "value": 10 + }, + "pollution": { + "type": "Float", + "value": 43.4 + }, + "configuration": { + "type": "Object", + "value": { + "firmware": { + "version": "1.1.0", + "hash": "cf23df2207d99a74fbe169e3eba035e633b65d94" + } + } + }, + "tags": { + "type": "Array", + "value": [ + "iot", + "device" + ] + }, + "enabled": { + "type": "Boolean", + "value": true + } + } + ] +} diff --git a/test/unit/ngsiv2/contextRequests/ngsildPayloadMeasure3.json b/test/unit/ngsiv2/contextRequests/ngsildPayloadMeasure3.json new file mode 100644 index 00000000..1ec520d3 --- /dev/null +++ b/test/unit/ngsiv2/contextRequests/ngsildPayloadMeasure3.json @@ -0,0 +1,109 @@ +{ + "actionType": "append", + "entities": [ + { + "id": "Second MQTT Device", + "type": "AnMQTTDevice", + "measure_id": { + "type": "Text", + "value": "urn:ngsi-ld:ParkingSpot:santander:daoiz_velarde_1_5:3" + }, + "measure_type": { + "type": "Text", + "value": "ParkingSpot" + }, + "status": { + "type": "Property", + "value": "free", + "metadata": { + "observedAt": { + "value": "2018-09-21T12:00:00Z" + } + } + }, + "category": { + "type": "Property", + "value": [ + "onstreet" + ] + }, + "refParkingSite": { + "type": "Relationship", + "value": "urn:ngsi-ld:ParkingSite:santander:daoiz_velarde_1_5" + }, + "name": { + "type": "Property", + "value": "A-13" + }, + "location": { + "type": "GeoProperty", + "value": { + "type": "Point", + "coordinates": [ + -3.80356167695194, + 43.46296641666926 + ] + } + }, + "@context": { + "type": "@context", + "value": [ + "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context.jsonld", + "https://schema.lab.fiware.org/ld/context" + ] + } + }, + { + "id": "Second MQTT Device", + "type": "AnMQTTDevice", + "measure_id": { + "type": "Text", + "value": "urn:ngsi-ld:ParkingSpot:santander:reyes_magos_1_1:1" + }, + "measure_type": { + "type": "Text", + "value": "ParkingSpot" + }, + "status": { + "type": "Property", + "value": "free", + "metadata": { + "observedAt": { + "value": "2012-09-21T12:00:00Z" + } + } + }, + "category": { + "type": "Property", + "value": [ + "onstreet" + ] + }, + "refParkingSite": { + "type": "Relationship", + "value": "urn:ngsi-ld:ParkingSite:santander:reyes_magos_1_1" + }, + "name": { + "type": "Property", + "value": "A-12" + }, + "location": { + "type": "GeoProperty", + "value": { + "type": "Point", + "coordinates": [ + -3.90356167695194, + 42.46296641666926 + ] + } + }, + "@context": { + "type": "@context", + "value": [ + "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context.jsonld", + "https://schema.lab.fiware.org/ld/context" + ] + } + } + ] + } diff --git a/test/unit/ngsiv2/contextRequests/ngsiv2PayloadMeasure3.json b/test/unit/ngsiv2/contextRequests/ngsiv2PayloadMeasure3.json new file mode 100644 index 00000000..ceb4c439 --- /dev/null +++ b/test/unit/ngsiv2/contextRequests/ngsiv2PayloadMeasure3.json @@ -0,0 +1,129 @@ +{ + "actionType": "append", + "entities": [ + { + "id": "Second MQTT Device", + "type": "AnMQTTDevice", + "measure_id": { + "type": "Text", + "value": "urn:ngsiv2:Streetlight:Streetlight-Mylightpoint-2" + }, + "measure_type": { + "type": "Text", + "value": "Streetlight" + }, + "name": { + "type": "Text", + "value": "MyLightPoint-test1" + }, + "description": { + "type": "Text", + "value": "testdescription" + }, + "status": { + "type": "Text", + "value": "connected", + "metadata": { + "TimeInstant": { + "type": "DateTime", + "value": "2023-11-17T11:59:22.661Z" + } + } + }, + "dateServiceStarted": { + "type": "DateTime", + "value": "2020-06-04T09: 55: 02" + }, + "locationComment": { + "type": "Text", + "value": "Test1" + }, + "location": { + "type": "geo:json", + "value": { + "coordinates": [ + -87.88429, + 41.99499 + ], + "type": "Point" + } + }, + "address": { + "type": "Text", + "value": { + "streetAddress": "MyStreet" + } + }, + "isRemotelyManaged": { + "type": "Integer", + "value": 1 + }, + "installationDate": { + "type": "DateTime", + "value": "2022-04-17T02: 30: 04" + } + }, + { + "id": "Second MQTT Device", + "type": "AnMQTTDevice", + "measure_id": { + "type": "Text", + "value": "urn:ngsiv2:Streetlight:Streetlight-Mylightpoint-3" + }, + "measure_type": { + "type": "Text", + "value": "Streetlight" + }, + "name": { + "type": "Text", + "value": "MyLightPoint-test2" + }, + "description": { + "type": "Text", + "value": "testdescription" + }, + "status": { + "type": "Text", + "value": "connected", + "metadata": { + "TimeInstant": { + "type": "DateTime", + "value": "2023-11-17T11:59:22.661Z" + } + } + }, + "dateServiceStarted": { + "type": "DateTime", + "value": "2022-06-04T09: 55: 02" + }, + "locationComment": { + "type": "Text", + "value": "Test3" + }, + "location": { + "type": "geo:json", + "value": { + "coordinates": [ + -84.88429, + 42.99499 + ], + "type": "Point" + } + }, + "address": { + "type": "Text", + "value": { + "streetAddress": "MyFarStreet" + } + }, + "isRemotelyManaged": { + "type": "Integer", + "value": 3 + }, + "installationDate": { + "type": "DateTime", + "value": "2023-04-17T02: 30: 04" + } + } + ] + } diff --git a/test/unit/ngsiv2/contextRequests/timestampMeasure3.json b/test/unit/ngsiv2/contextRequests/timestampMeasure3.json new file mode 100644 index 00000000..da11a952 --- /dev/null +++ b/test/unit/ngsiv2/contextRequests/timestampMeasure3.json @@ -0,0 +1,37 @@ +{ + "actionType": "append", + "entities": [ + { + "id": "Second MQTT Device", + "type": "AnMQTTDevice", + "temperature": { + "type": "celsius", + "value": "87" + }, + "humidity": { + "type": "degrees", + "value": "32" + }, + "TimeInstant": { + "type": "DateTime", + "value": "2007-11-03T13:18:05Z" + } + }, + { + "id": "Second MQTT Device", + "type": "AnMQTTDevice", + "temperature": { + "type": "celsius", + "value": "89" + }, + "humidity": { + "type": "degrees", + "value": "33" + }, + "TimeInstant": { + "type": "DateTime", + "value": "2007-11-03T13:18:06Z" + } + } + ] + } diff --git a/test/unit/ngsiv2/contextRequests/unknownMeasures3.json b/test/unit/ngsiv2/contextRequests/unknownMeasures3.json new file mode 100644 index 00000000..07b17932 --- /dev/null +++ b/test/unit/ngsiv2/contextRequests/unknownMeasures3.json @@ -0,0 +1,29 @@ +{ + "actionType": "append", + "entities": [ + { + "id": "Second MQTT Device", + "type": "AnMQTTDevice", + "humidity": { + "type": "degrees", + "value": "32" + }, + "weight": { + "type": "Text", + "value": "87" + } + }, + { + "id": "Second MQTT Device", + "type": "AnMQTTDevice", + "humidity": { + "type": "degrees", + "value": "33" + }, + "weight": { + "type": "Text", + "value": "89" + } + } + ] + } diff --git a/test/unit/ngsiv2/contextRequests/unprovisionedDevice4.json b/test/unit/ngsiv2/contextRequests/unprovisionedDevice4.json new file mode 100644 index 00000000..f8eabedb --- /dev/null +++ b/test/unit/ngsiv2/contextRequests/unprovisionedDevice4.json @@ -0,0 +1,29 @@ +{ + "actionType": "append", + "entities": [ + { + "id": "TheLightType:JSON_UNPROVISIONED", + "type": "TheLightType", + "humidity": { + "type": "Text", + "value": "32" + }, + "temperature": { + "type": "Text", + "value": "87" + } + }, + { + "id": "TheLightType:JSON_UNPROVISIONED", + "type": "TheLightType", + "humidity": { + "type": "Text", + "value": "33" + }, + "temperature": { + "type": "Text", + "value": "89" + } + } + ] + }