diff --git a/lib/services/ngsi/entities-NGSI-v2.js b/lib/services/ngsi/entities-NGSI-v2.js index 5330ca9d8..bb98bf864 100644 --- a/lib/services/ngsi/entities-NGSI-v2.js +++ b/lib/services/ngsi/entities-NGSI-v2.js @@ -662,7 +662,22 @@ function sendUpdateValueNgsi2(entityName, originMeasures, originTypeInformation, transformedObject.type = entityAttrs.type; options.json = transformedObject; options.method = 'POST'; - } // else: keep current options object created for a batch update + } else { + // keep current options object created for a batch update + // but try sort entities by TimeInstant + if (payload.entities.every((entity) => 'TimeInstant' in entity)) { + payload.entities.sort( + (a, b) => new Date(a.TimeInstant.value).getTime() - new Date(b.TimeInstant.value).getTime() + ); + options.json = payload; + } else { + logger.debug( + context, + "some entities lack the 'TimeInstant' key. Sorting is not feasible: %j ", + payload.entities + ); + } + } //Send the NGSI request logger.debug(context, 'Updating device value in the Context Broker at: %j', options.url); diff --git a/test/functional/testCases.js b/test/functional/testCases.js index cc7169c35..4efc88c6e 100644 --- a/test/functional/testCases.js +++ b/test/functional/testCases.js @@ -446,7 +446,7 @@ const testCases = [ ] }, { - describeName: '0021 Simple group with active attributes with metadata', + describeName: '0021 - Simple group with active attributes with metadata', provision: { url: 'http://localhost:' + config.iota.server.port + '/iot/services', method: 'POST', @@ -514,7 +514,7 @@ const testCases = [ ] }, { - describeName: '0022 Simple group with active attributes and multimeasures', + describeName: '0022 - Simple group with active attributes and multimeasures', provision: { url: 'http://localhost:' + config.iota.server.port + '/iot/services', method: 'POST', @@ -642,7 +642,7 @@ const testCases = [ }, { shouldName: - 'A - WHEN sending defined object_ids (measures) through http IT should send measures with TimeInstant to Context Broker preserving value types and name mappings', + 'A - WHEN sending defined object_ids (measures) through http IT should send measures with TimeInstant to Context Broker preserving value types and name mappings and order', type: 'multimeasure', measure: { url: 'http://localhost:' + config.http.port + '/iot/json', @@ -771,6 +771,138 @@ const testCases = [ } ] } + }, + { + shouldName: + 'A - WHEN sending defined object_ids (measures) through http IT should send measures with TimeInstant to Context Broker preserving value types and name mappings and sorted by TimeInstant', + type: 'multimeasure', + measure: { + url: 'http://localhost:' + config.http.port + '/iot/json', + method: 'POST', + qs: { + i: globalEnv.deviceId, + k: globalEnv.apikey + }, + json: [ + { + a: 0, + TimeInstant: '2024-04-10T10:15:00Z' + }, + { + a: 1, + TimeInstant: '2024-04-10T10:05:00Z' + }, + { + a: 2, + TimeInstant: '2024-04-10T10:20:00Z' + }, + { + a: 3, + TimeInstant: '2024-04-10T10:00:00Z' + }, + { + a: 4, + TimeInstant: '2024-04-10T10:10:00Z' + }, + { + a: 5, + TimeInstant: '2024-04-10T10:30:00Z' + }, + { + a: 6, + TimeInstant: '2024-04-10T10:25:00Z' + } + ] + }, + expectation: { + actionType: 'append', + entities: [ + { + attr_a: { + type: 'Number', + value: 3 + }, + TimeInstant: { + type: 'DateTime', + value: '2024-04-10T10:00:00Z' + }, + id: globalEnv.entity_name, + type: globalEnv.entity_type + }, + { + attr_a: { + type: 'Number', + value: 1 + }, + TimeInstant: { + type: 'DateTime', + value: '2024-04-10T10:05:00Z' + }, + id: globalEnv.entity_name, + type: globalEnv.entity_type + }, + { + attr_a: { + type: 'Number', + value: 4 + }, + TimeInstant: { + type: 'DateTime', + value: '2024-04-10T10:10:00Z' + }, + id: globalEnv.entity_name, + type: globalEnv.entity_type + }, + { + attr_a: { + type: 'Number', + value: 0 + }, + TimeInstant: { + type: 'DateTime', + value: '2024-04-10T10:15:00Z' + }, + id: globalEnv.entity_name, + type: globalEnv.entity_type + }, + { + attr_a: { + type: 'Number', + value: 2 + }, + TimeInstant: { + type: 'DateTime', + value: '2024-04-10T10:20:00Z' + }, + id: globalEnv.entity_name, + type: globalEnv.entity_type + }, + { + attr_a: { + type: 'Number', + value: 6 + }, + TimeInstant: { + type: 'DateTime', + value: '2024-04-10T10:25:00Z' + }, + id: globalEnv.entity_name, + type: globalEnv.entity_type + }, + { + attr_a: { + type: 'Number', + value: 5 + }, + TimeInstant: { + type: 'DateTime', + value: '2024-04-10T10:30:00Z' + }, + id: globalEnv.entity_name, + type: globalEnv.entity_type + } + ] + } } ] }, @@ -2879,56 +3011,56 @@ const testCases = [ actionType: 'append', entities: [ { - id: globalEnv.entity_name, + id: 'TestType:TestDevice1', type: globalEnv.entity_type, - a: { + a1: { value: 23, type: 'Text', metadata: { TimeInstant: { - value: _.isDateString, + value: '2011-01-01T01:11:11.111Z', type: 'DateTime' } } }, TimeInstant: { - value: _.isDateString, + value: '2011-01-01T01:11:11.111Z', type: 'DateTime' } }, { - id: 'TestType:TestDevice1', + id: 'TestType:TestDevice2', type: globalEnv.entity_type, - a1: { + a2: { value: 23, type: 'Text', metadata: { TimeInstant: { - value: '2011-01-01T01:11:11.111Z', + value: '2022-02-02T02:22:22.222Z', type: 'DateTime' } } }, TimeInstant: { - value: '2011-01-01T01:11:11.111Z', + value: '2022-02-02T02:22:22.222Z', type: 'DateTime' } }, { - id: 'TestType:TestDevice2', + id: globalEnv.entity_name, type: globalEnv.entity_type, - a2: { + a: { value: 23, type: 'Text', metadata: { TimeInstant: { - value: '2022-02-02T02:22:22.222Z', + value: _.isDateString, type: 'DateTime' } } }, TimeInstant: { - value: '2022-02-02T02:22:22.222Z', + value: _.isDateString, type: 'DateTime' } }