iotagent-node-lib 4.0.1 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -45,7 +45,7 @@ jobs:
45
45
  runs-on: ubuntu-latest
46
46
  services:
47
47
  mongodb:
48
- image: mongo:4.2
48
+ image: mongo:6.0
49
49
  ports:
50
50
  - 27017:27017
51
51
  strategy:
@@ -72,7 +72,7 @@ jobs:
72
72
  needs: unit-test
73
73
  services:
74
74
  mongodb:
75
- image: mongo:4.2
75
+ image: mongo:6.0
76
76
  ports:
77
77
  - 27017:27017
78
78
  steps:
@@ -0,0 +1 @@
1
+
package/doc/api.md CHANGED
@@ -450,7 +450,7 @@ propagated to NGSI interface (note that in this case the value of `explicitAttrs
450
450
  that looks likes a JSON). This is necessary when same attribute names are used within multiple entities. Only static
451
451
  attributes included in that array will be propagated to NGSI interface.
452
452
 
453
- Note that in the previous case show above, when selecting the object_id (with `{object_id:'active_id'}`), the attribute
453
+ Note that in the previous case show above, when selecting the object_id (with `{object_id:'active_id'}`), the attribute
454
454
  must be defined. In other words, it would not work if the attribute with the corresponding `object_id`, is not defined.
455
455
 
456
456
  Case 5:
@@ -1217,6 +1217,8 @@ Config group is represented by a JSON object with the following fields:
1217
1217
  | `defaultEntityNameConjunction` | ✓ | string | | optional string value to set default conjunction string used to compose a default `entity_name` when is not provided at device provisioning time. |
1218
1218
  | `autoprovision` | ✓ | bool | ✓? | optional boolean: If `false`, autoprovisioned devices (i.e. devices that are not created with an explicit provision operation but when the first measure arrives) are not allowed in this group. Default (in the case of omitting the field) is `true`. |
1219
1219
  | `payloadType` | ✓ | string | | optional string value used to switch between **IoTAgent**, **NGSI-v2** and **NGSI-LD** measure payloads types. Possible values are: `iotagent`, `ngsiv2` or `ngsild`. The default is `iotagent`. |
1220
+ | `transport` | ✓ | `string` | | Transport protocol used by the group of devices to send updates, for the IoT Agents with multiple transport protocols. |
1221
+ | `endpoint` | ✓ | `string` | | Endpoint where the group of device is going to receive commands, if any. |
1220
1222
 
1221
1223
  ### Config group operations
1222
1224
 
@@ -1,4 +1,4 @@
1
- ARG IMAGE_TAG=12.1-slim
1
+ ARG IMAGE_TAG=12.4-slim
2
2
  FROM debian:${IMAGE_TAG}
3
3
 
4
4
  ARG CLEAN_DEV_TOOLS
@@ -3,6 +3,6 @@ version: '3'
3
3
  services:
4
4
 
5
5
  mongo:
6
- image: mongo:4.4
6
+ image: mongo:6.0.12
7
7
  ports:
8
8
  - "27017:27017"
@@ -308,6 +308,7 @@ exports.getDeviceByName = deviceService.getDeviceByName;
308
308
  exports.getDeviceByNameAndType = deviceService.getDeviceByNameAndType;
309
309
  exports.getDevicesByAttribute = deviceService.getDevicesByAttribute;
310
310
  exports.mergeDeviceWithConfiguration = deviceService.mergeDeviceWithConfiguration;
311
+ exports.findOrCreate = deviceService.findOrCreate;
311
312
  exports.retrieveDevice = deviceService.retrieveDevice;
312
313
  exports.getConfiguration = groupConfig.get;
313
314
  exports.getConfigurationSilently = groupConfig.getSilently;
@@ -45,6 +45,8 @@ const Group = new Schema({
45
45
  url: String,
46
46
  resource: String,
47
47
  apikey: String,
48
+ endpoint: String,
49
+ transport: String,
48
50
  type: String,
49
51
  service: { type: String, lowercase: true },
50
52
  subservice: String,
@@ -61,7 +61,9 @@ function register(callback) {
61
61
  defaultEntityNameConjunction: service.defaultEntityNameConjunction,
62
62
  ngsiVersion: service.ngsiVersion,
63
63
  entityNameExp: service.entityNameExp,
64
- payloadType: service.payloadType
64
+ payloadType: service.payloadType,
65
+ endpoint: service.endpoint,
66
+ transport: service.transport
65
67
  };
66
68
  }
67
69
 
@@ -309,6 +309,7 @@ function update(device, callback) {
309
309
  data.internalAttributes = device.internalAttributes;
310
310
  data.commands = device.commands;
311
311
  data.endpoint = device.endpoint;
312
+ data.transport = device.transport;
312
313
  data.polling = device.polling;
313
314
  data.name = device.name;
314
315
  data.type = device.type;
@@ -352,7 +352,7 @@ function registerDevice(deviceObj, callback) {
352
352
  deviceObj.subservice = deviceData.subservice;
353
353
  deviceObj.type = deviceData.type;
354
354
  deviceObj.staticAttributes = deviceObj.staticAttributes ? deviceObj.staticAttributes : [];
355
- deviceObj.commands = deviceObj.commands ? deviceObj.commands : [];
355
+ deviceObj.commands = deviceData.commands ? deviceData.commands : [];
356
356
  deviceObj.lazy = deviceObj.lazy ? deviceObj.lazy : [];
357
357
  if ('apikey' in deviceData && deviceData.apikey !== undefined) {
358
358
  deviceObj.apikey = deviceData.apikey;
@@ -677,7 +677,7 @@ function retrieveDevice(deviceId, apiKey, callback) {
677
677
  } else {
678
678
  async.waterfall(
679
679
  [
680
- apply(groupService.get, config.getConfig().defaultResource || '', apiKey),
680
+ apply(groupService.getSilently, config.getConfig().defaultResource || '', apiKey),
681
681
  apply(findOrCreate, deviceId, apiKey),
682
682
  apply(
683
683
  mergeDeviceWithConfiguration,
@@ -704,5 +704,6 @@ exports.unregister = intoTrans(context, unregisterDevice);
704
704
  exports.clearRegistry = intoTrans(context, checkRegistry)(clearRegistry);
705
705
  exports.retrieveDevice = intoTrans(context, checkRegistry)(retrieveDevice);
706
706
  exports.mergeDeviceWithConfiguration = mergeDeviceWithConfiguration;
707
+ exports.findOrCreate = findOrCreate;
707
708
  exports.findConfigurationGroup = findConfigurationGroup;
708
709
  exports.init = init;
@@ -273,7 +273,12 @@ function updateRegisterDeviceNgsi2(deviceObj, entityInfoUpdated, callback) {
273
273
  if ('payloadType' in newDevice && newDevice.payloadType !== undefined) {
274
274
  oldDevice.payloadType = newDevice.payloadType;
275
275
  }
276
- oldDevice.endpoint = newDevice.endpoint || oldDevice.endpoint;
276
+ if ('endpoint' in newDevice && newDevice.endpoint !== undefined) {
277
+ oldDevice.endpoint = newDevice.endpoint;
278
+ }
279
+ if ('transport' in newDevice && newDevice.transport !== undefined) {
280
+ oldDevice.transport = newDevice.transport;
281
+ }
277
282
 
278
283
  callback(null, oldDevice);
279
284
  } else {
@@ -61,7 +61,7 @@ function createGroup(group, callback) {
61
61
 
62
62
  logger.debug(
63
63
  context,
64
- 'Storing device group for service [%s] and subservice [%s]',
64
+ 'Storing device group id %s for service [%s] and subservice [%s]',
65
65
  storeGroup._id,
66
66
  storeGroup.service,
67
67
  storeGroup.subservice
@@ -144,6 +144,7 @@ function find(service, subservice, callback) {
144
144
  }
145
145
 
146
146
  if (result.length > 0) {
147
+ logger.debug(context, 'groups found %j', result);
147
148
  return callback(null, {
148
149
  count: result.length,
149
150
  services: result
@@ -169,7 +170,7 @@ function findBy(fields) {
169
170
  /* eslint-disable-next-line prefer-rest-params */
170
171
  const callback = arguments[i];
171
172
 
172
- logger.debug(context, 'Looking for device params %j', fields);
173
+ logger.debug(context, 'Looking for group with params %j', fields);
173
174
 
174
175
  for (const p in registeredGroups) {
175
176
  if (registeredGroups.hasOwnProperty(p)) {
@@ -189,6 +190,7 @@ function findBy(fields) {
189
190
  }
190
191
 
191
192
  if (result) {
193
+ logger.debug(context, 'group found %j', result);
192
194
  callback(null, result);
193
195
  } else {
194
196
  callback(new errors.DeviceGroupNotFound('n/a', 'n/a'));
@@ -211,6 +213,7 @@ function getSingleGroup(resource, apikey, callback) {
211
213
  }
212
214
 
213
215
  if (result) {
216
+ logger.debug(context, 'single group found %j', result);
214
217
  callback(null, result);
215
218
  } else {
216
219
  callback(new errors.DeviceGroupNotFound(resource, apikey));
@@ -228,6 +231,7 @@ function getSingleGroupType(type, callback) {
228
231
  }
229
232
 
230
233
  if (result) {
234
+ logger.debug(context, 'single group type found %j', result);
231
235
  callback(null, result);
232
236
  } else {
233
237
  callback(new errors.DeviceGroupNotFound(type));
@@ -243,7 +247,7 @@ function update(id, body, callback) {
243
247
  groupToModify[i] = body[i];
244
248
  }
245
249
  }
246
-
250
+ logger.debug(context, 'groupd to update %j', groupToModify);
247
251
  callback(null, groupToModify);
248
252
  } else {
249
253
  callback(new errors.DeviceGroupNotFound(id));
@@ -262,8 +266,8 @@ exports.list = intoTrans(context, listGroups);
262
266
  exports.init = intoTrans(context, init);
263
267
  exports.find = intoTrans(context, find);
264
268
  exports.findBy = intoTrans(context, findBy);
265
- exports.findType = intoTrans(context, findBy(['service', 'subservice', 'type']));
266
- exports.findTypeSilently = intoTrans(context, findBy(['service', 'subservice', 'type']));
269
+ exports.findType = intoTrans(context, findBy(['service', 'subservice', 'type', 'apikey']));
270
+ exports.findTypeSilently = intoTrans(context, findBy(['service', 'subservice', 'type', 'apikey']));
267
271
  exports.findSilently = intoTrans(context, findBy(['service', 'subservice']));
268
272
  exports.get = intoTrans(context, getSingleGroup);
269
273
  exports.getSilently = intoTrans(context, getSingleGroup);
@@ -41,6 +41,8 @@ const attributeList = [
41
41
  'resource',
42
42
  'apikey',
43
43
  'type',
44
+ 'endpoint',
45
+ 'transport',
44
46
  'service',
45
47
  'subservice',
46
48
  'description',
@@ -32,6 +32,10 @@
32
32
  "description": "Protocol the device is using to communicate with the platform",
33
33
  "type": "string"
34
34
  },
35
+ "endpoint": {
36
+ "description": "Endpoint for the commands targeting this device",
37
+ "type": "string"
38
+ },
35
39
  "transport": {
36
40
  "description": "Transport protocol used by the platform to communicate with the device",
37
41
  "type": "string"
@@ -32,6 +32,10 @@
32
32
  "description": "Protocol the device is using to communicate with the platform",
33
33
  "type": "string"
34
34
  },
35
+ "endpoint": {
36
+ "description": "Endpoint for the commands targeting this device",
37
+ "type": "string"
38
+ },
35
39
  "transport": {
36
40
  "description": "Transport protocol used by the platform to communicate with the device",
37
41
  "type": "string"
@@ -20,6 +20,14 @@
20
20
  "type": "string",
21
21
  "required": true
22
22
  },
23
+ "endpoint": {
24
+ "description": "Endpoint for the commands targeting this group",
25
+ "type": "string"
26
+ },
27
+ "transport": {
28
+ "description": "Transport protocol used by the platform to communicate with the device",
29
+ "type": "string"
30
+ },
23
31
  "token": {
24
32
  "description": "token",
25
33
  "type": "string"
@@ -33,6 +33,10 @@
33
33
  "description": "Endpoint for the commands targeting this device",
34
34
  "type": "string"
35
35
  },
36
+ "transport": {
37
+ "description": "Transport protocol used by the platform to communicate with the device",
38
+ "type": "string"
39
+ },
36
40
  "lazy": {
37
41
  "description": "list of lazy attributes of the devices",
38
42
  "type": "array",
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "iotagent-node-lib",
3
3
  "license": "AGPL-3.0-only",
4
4
  "description": "IoT Agent library to interface with NGSI Context Broker",
5
- "version": "4.0.1",
5
+ "version": "4.1.0",
6
6
  "homepage": "https://github.com/telefonicaid/iotagent-node-lib",
7
7
  "keywords": [
8
8
  "fiware",
@@ -30,10 +30,10 @@ test cases are automatically generated. Each test case is defined as an object w
30
30
  - `json`: The JSON object that defines the group
31
31
  - `headers`: The headers to send to the provisioning API. This should contain the `fiware-service` and
32
32
  `fiware-servicepath` headers.
33
- - `skip`: optional. If set to `true`, the test case (`describe`) will be skipped. This is useful to skip test
34
- cases that are not supported by the agent. It can also have a string value `"lib"`. This will skip the test case
35
- only if the is executed in the IoTA Node lib repo. This is useful to skip test cases that are not supported by
36
- the lib (I.E: all tests related to the transport).
33
+ - `skip`: optional. Allow to skip test cases (at `describe` level). It allows diferent values: `false` (default, meaning that the test is not skipped in any circustance),
34
+ `true` (meaning the test is always skipped), `"lib"` (meaning the test has to be skipped when running it in IoTA Node lib repo) and
35
+ `"json"` (meaning the test has to be skipped when running it in IOTA JSON repo). The latter alternatives are useful to skip test cases that are not supported by
36
+ the lib (I.E: all tests related to the transport) or by the IOTA. Combinations (e.g `"lib,json"`) and negation (e.g. `"!lib"`) are also supported.
37
37
  - `should`: The array of test cases to execute. Each test case is defined as an object with the following elements:
38
38
  - `transport`: The transport to use to send the measure. This can be `HTTP` or `MQTT`. It uses `HTTP` by default
39
39
  or if the `transport` element is not defined. See the "Advanced features" section for more information.
@@ -88,7 +88,7 @@ describe('FUNCTIONAL TESTS AUTO', function () {
88
88
 
89
89
  testCase.should.forEach((should) => {
90
90
  it(should.shouldName, async function () {
91
- if (testCase.skip && testUtils.checkSkip(testCase.skip, 'lib')) {
91
+ if (should.skip && testUtils.checkSkip(should.skip, 'lib')) {
92
92
  this.skip();
93
93
  }
94
94
  // Skip the test if the transport is specified (IoTA Lib does not support any transport)
@@ -1476,6 +1476,65 @@ const testCases = [
1476
1476
  }
1477
1477
  ]
1478
1478
  },
1479
+ // 0200 - COMMANDS TESTS
1480
+ {
1481
+ describeName: '0200 - Simple group with commands',
1482
+ provision: {
1483
+ url: 'http://localhost:' + config.iota.server.port + '/iot/services',
1484
+ method: 'POST',
1485
+ json: {
1486
+ services: [
1487
+ {
1488
+ resource: '/iot/json',
1489
+ apikey: globalEnv.apikey,
1490
+ entity_type: globalEnv.entity_type,
1491
+ commands: [
1492
+ {
1493
+ name: 'cmd1',
1494
+ type: 'command'
1495
+ }
1496
+ ],
1497
+ endpoint: 'http://myendpoint.com',
1498
+ transport: 'http',
1499
+ lazy: [],
1500
+ attributes: [],
1501
+ static_attributes: []
1502
+ }
1503
+ ]
1504
+ },
1505
+ headers: {
1506
+ 'fiware-service': globalEnv.service,
1507
+ 'fiware-servicepath': globalEnv.servicePath
1508
+ }
1509
+ },
1510
+ should: [
1511
+ {
1512
+ shouldName:
1513
+ 'A - WHEN sending not provisioned object_ids (measures) through http IT should store commands into Context Broker',
1514
+ type: 'single',
1515
+ skip: '!lib', // there is not CB registration mock
1516
+ measure: {
1517
+ url: 'http://localhost:' + config.http.port + '/iot/json',
1518
+ method: 'POST',
1519
+ qs: {
1520
+ i: globalEnv.deviceId,
1521
+ k: globalEnv.apikey
1522
+ },
1523
+ json: {
1524
+ b: 10
1525
+ }
1526
+ },
1527
+ expectation: {
1528
+ id: globalEnv.entity_name,
1529
+ type: globalEnv.entity_type,
1530
+ b: {
1531
+ value: 10,
1532
+ type: 'string'
1533
+ }
1534
+ }
1535
+ }
1536
+ ]
1537
+ },
1479
1538
  // 0300 - STATIC ATTRIBUTES TESTS
1480
1539
  {
1481
1540
  describeName:
@@ -2424,6 +2483,7 @@ const testCases = [
2424
2483
  {
2425
2484
  shouldName: 'C - WHEN sending only b IT should store only [{object_id:b}] attrs into Context Broker',
2426
2485
  type: 'single',
2486
+ skip: '!lib', // FIXME: https://github.com/telefonicaid/iotagent-json/issues/782
2427
2487
  measure: {
2428
2488
  url: 'http://localhost:' + config.http.port + '/iot/json',
2429
2489
  method: 'POST',
@@ -2450,6 +2510,7 @@ const testCases = [
2450
2510
  shouldName:
2451
2511
  'D - WHEN no sending any defined case IT should store [static_a,static_b] attrs into Context Broker',
2452
2512
  type: 'single',
2513
+ skip: '!lib', // FIXME: https://github.com/telefonicaid/iotagent-json/issues/782
2453
2514
  measure: {
2454
2515
  url: 'http://localhost:' + config.http.port + '/iot/json',
2455
2516
  method: 'POST',
@@ -139,6 +139,8 @@ function groupToIoTAConfigType(group, service, subservice) {
139
139
  type.type = group.entity_type;
140
140
  } else if (key === 'static_attributes') {
141
141
  type.staticAttributes = group.static_attributes;
142
+ } else if (key === 'commands') {
143
+ type.commands = group.commands;
142
144
  } else if (key !== 'resource') {
143
145
  type[key] = group[key];
144
146
  }
@@ -61,6 +61,8 @@ const optionsCreation = {
61
61
  entity_type: 'SensorMachine',
62
62
  trust: '8970A9078A803H3BL98PINEQRW8342HBAMS',
63
63
  cbHost: 'http://unexistentHost:1026',
64
+ transport: 'HTTP',
65
+ endpoint: 'http://myendpoint.com',
64
66
  commands: [
65
67
  {
66
68
  name: 'wheel1',
@@ -136,6 +138,8 @@ const optionsUpdate = {
136
138
  json: {
137
139
  trust: '8970A9078A803H3BL98PINEQRW8342HBAMS',
138
140
  cbHost: 'http://anotherUnexistentHost:1026',
141
+ transport: 'MQTT',
142
+ endpoint: 'http://yourendpoint.com',
139
143
  commands: [
140
144
  {
141
145
  name: 'wheel1',
@@ -217,6 +221,8 @@ describe('NGSI-v2 - Device Group Configuration API', function () {
217
221
  request(optionsList, function (error, response, body) {
218
222
  body.count.should.equal(1);
219
223
  body.services[0].apikey.should.equal('801230BJKL23Y9090DSFL123HJK09H324HV8732');
224
+ body.services[0].transport.should.equal('HTTP');
225
+ body.services[0].endpoint.should.equal('http://myendpoint.com');
220
226
  done();
221
227
  });
222
228
  });
@@ -664,6 +670,8 @@ describe('NGSI-v2 - Device Group Configuration API', function () {
664
670
  ) {
665
671
  body.services[i].cbHost.should.equal('http://anotherUnexistentHost:1026');
666
672
  body.services[i].static_attributes.length.should.equal(1);
673
+ body.services[i].endpoint = 'http://yourendpoint.com';
674
+ body.services[i].transport = 'MQTT';
667
675
  found = true;
668
676
  }
669
677
  }