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.
- package/.github/workflows/ci.yml +2 -2
- package/CHANGES_NEXT_RELEASE +1 -0
- package/doc/api.md +3 -1
- package/docker/Mosquitto/Dockerfile +1 -1
- package/docker-compose-dev.yml +1 -1
- package/lib/fiware-iotagent-lib.js +1 -0
- package/lib/model/Group.js +2 -0
- package/lib/services/common/iotManagerService.js +3 -1
- package/lib/services/devices/deviceRegistryMongoDB.js +1 -0
- package/lib/services/devices/deviceService.js +3 -2
- package/lib/services/devices/devices-NGSI-v2.js +6 -1
- package/lib/services/groups/groupRegistryMemory.js +9 -5
- package/lib/services/groups/groupRegistryMongoDB.js +2 -0
- package/lib/templates/createDevice.json +4 -0
- package/lib/templates/createDeviceLax.json +4 -0
- package/lib/templates/deviceGroup.json +8 -0
- package/lib/templates/updateDevice.json +4 -0
- package/package.json +1 -1
- package/test/functional/README.md +4 -4
- package/test/functional/functional-tests-runner.js +1 -1
- package/test/functional/testCases.js +61 -0
- package/test/functional/testUtils.js +2 -0
- package/test/unit/ngsiv2/provisioning/device-group-api-test.js +8 -0
package/.github/workflows/ci.yml
CHANGED
|
@@ -45,7 +45,7 @@ jobs:
|
|
|
45
45
|
runs-on: ubuntu-latest
|
|
46
46
|
services:
|
|
47
47
|
mongodb:
|
|
48
|
-
image: mongo:
|
|
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:
|
|
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
|
|
package/docker-compose-dev.yml
CHANGED
|
@@ -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;
|
package/lib/model/Group.js
CHANGED
|
@@ -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 =
|
|
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.
|
|
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
|
-
|
|
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
|
|
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);
|
|
@@ -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
|
|
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.
|
|
34
|
-
|
|
35
|
-
|
|
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 (
|
|
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
|
}
|