iotagent-node-lib 4.6.0 → 4.8.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 -3
- package/CHANGES_NEXT_RELEASE +1 -0
- package/Changelog +20 -0
- package/config.js +3 -1
- package/doc/README.md +1 -0
- package/doc/admin.md +39 -5
- package/doc/api.md +74 -9
- package/doc/devel/northboundinteractions.md +122 -15
- package/doc/models/models.md +260 -0
- package/doc/requirements.txt +1 -1
- package/docker/Mosquitto/Dockerfile +1 -1
- package/lib/commonConfig.js +21 -2
- package/lib/fiware-iotagent-lib.js +15 -11
- package/lib/jexlTranformsMap.js +182 -35
- package/lib/model/Command.js +11 -2
- package/lib/model/Device.js +8 -3
- package/lib/model/Group.js +5 -3
- package/lib/model/dbConn.js +53 -112
- package/lib/services/commands/commandRegistryMongoDB.js +130 -76
- package/lib/services/commands/commandService.js +3 -3
- package/lib/services/common/iotManagerService.js +3 -1
- package/lib/services/devices/deviceRegistryMemory.js +36 -0
- package/lib/services/devices/deviceRegistryMongoDB.js +161 -88
- package/lib/services/devices/deviceService.js +44 -5
- package/lib/services/devices/devices-NGSI-v2.js +6 -1
- package/lib/services/groups/groupRegistryMongoDB.js +120 -83
- package/lib/services/ngsi/entities-NGSI-v2.js +14 -1
- package/lib/services/ngsi/ngsiService.js +32 -1
- package/lib/services/northBound/contextServer-NGSI-v2.js +12 -3
- package/lib/services/northBound/contextServer.js +2 -1
- package/lib/services/northBound/deviceProvisioningServer.js +12 -3
- package/lib/services/northBound/northboundServer.js +1 -0
- package/lib/templates/createDevice.json +4 -0
- package/lib/templates/updateDevice.json +16 -0
- package/lib/templates/updateDeviceLax.json +12 -0
- package/package.json +4 -4
- package/test/functional/config-test.js +3 -2
- package/test/functional/testUtils.js +15 -4
- package/test/unit/examples/groupProvisioningRequests/provisionFullGroup.json +1 -0
- package/test/unit/expressions/jexlExpression-test.js +165 -1
- package/test/unit/general/config-multi-core-test.js +1 -2
- package/test/unit/general/contextBrokerKeystoneSecurityAccess-test.js +5 -4
- package/test/unit/general/deviceService-test.js +6 -5
- package/test/unit/memoryRegistry/deviceRegistryMemory_test.js +6 -5
- package/test/unit/mongodb/mongodb-connectionoptions-test.js +7 -39
- package/test/unit/ngsi-ld/expressions/jexlBasedTransformations-test.js +1 -2
- package/test/unit/ngsi-ld/general/config-jsonld-contexts-test.js +1 -2
- package/test/unit/ngsi-mixed/provisioning/ngsi-versioning-test.js +8 -5
- package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +42 -41
- package/test/unit/ngsiv2/general/contextBrokerOAuthSecurityAccess-test.js +11 -10
- package/test/unit/ngsiv2/general/deviceService-test.js +6 -5
- package/test/unit/ngsiv2/general/https-support-test.js +1 -1
- package/test/unit/ngsiv2/lazyAndCommands/active-devices-attribute-update-test.js +4 -3
- package/test/unit/ngsiv2/lazyAndCommands/command-test.js +6 -5
- package/test/unit/ngsiv2/lazyAndCommands/lazy-devices-test.js +17 -16
- package/test/unit/ngsiv2/lazyAndCommands/polling-commands-test.js +10 -18
- package/test/unit/ngsiv2/ngsiService/active-devices-test.js +21 -20
- package/test/unit/ngsiv2/ngsiService/staticAttributes-test.js +8 -7
- package/test/unit/ngsiv2/plugins/alias-plugin_test.js +12 -11
- package/test/unit/ngsiv2/plugins/custom-plugin_test.js +3 -2
- package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +28 -27
- package/test/unit/ngsiv2/provisioning/device-group-api-test.js +6 -4
- package/test/unit/ngsiv2/provisioning/device-provisioning-api_test.js +12 -11
- package/test/unit/ngsiv2/provisioning/device-provisioning-configGroup-api_test.js +11 -10
- package/test/unit/ngsiv2/provisioning/device-registration_test.js +5 -4
- package/test/unit/ngsiv2/provisioning/listProvisionedDevices-test.js +6 -5
- package/test/unit/ngsiv2/provisioning/provisionDeviceMultientity-test.js +1 -1
- package/test/unit/ngsiv2/provisioning/removeProvisionedDevice-test.js +5 -4
- package/test/unit/ngsiv2/provisioning/updateProvisionedDevices-test.js +8 -7
- package/test/unit/ngsiv2/ngsiService/subscriptions-test.js +0 -326
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
const logger = require('logops');
|
|
27
|
-
const
|
|
27
|
+
const mongoose = require('mongoose');
|
|
28
28
|
const intoTrans = require('../common/domain').intoTrans;
|
|
29
29
|
const fillService = require('./../common/domain').fillService;
|
|
30
30
|
const alarmsInt = require('../common/alarmManagement').intercept;
|
|
@@ -61,28 +61,11 @@ const attributeList = [
|
|
|
61
61
|
'defaultEntityNameConjunction',
|
|
62
62
|
'ngsiVersion',
|
|
63
63
|
'entityNameExp',
|
|
64
|
-
'payloadType'
|
|
64
|
+
'payloadType',
|
|
65
|
+
'useCBflowControl',
|
|
66
|
+
'storeLastMeasure'
|
|
65
67
|
];
|
|
66
68
|
|
|
67
|
-
/**
|
|
68
|
-
* Generates a handler for the save device group operations. The handler will take the customary error and the saved
|
|
69
|
-
* device group as the parameters (and pass the serialized DAO as the callback value).
|
|
70
|
-
*
|
|
71
|
-
* @return {Function} The generated handler.
|
|
72
|
-
*/
|
|
73
|
-
function saveGroupHandler(groupDAO, callback) {
|
|
74
|
-
/* eslint-disable-next-line no-unused-vars */
|
|
75
|
-
return function saveHandler(error, result) {
|
|
76
|
-
if (error) {
|
|
77
|
-
logger.debug(fillService(context, groupDAO), 'Error storing device group information: %s', error);
|
|
78
|
-
|
|
79
|
-
callback(new errors.InternalDbError(error));
|
|
80
|
-
} else {
|
|
81
|
-
callback(null, groupDAO.toObject());
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
|
|
86
69
|
function createGroup(group, callback) {
|
|
87
70
|
/* eslint-disable-next-line new-cap */
|
|
88
71
|
const groupObj = new Group.model();
|
|
@@ -99,9 +82,12 @@ function createGroup(group, callback) {
|
|
|
99
82
|
groupObj.apikey,
|
|
100
83
|
groupObj.resource
|
|
101
84
|
);
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
85
|
+
groupObj
|
|
86
|
+
.save({})
|
|
87
|
+
.then((groupDAO) => {
|
|
88
|
+
callback(null, groupDAO.toObject());
|
|
89
|
+
})
|
|
90
|
+
.catch((error) => {
|
|
105
91
|
if (error.code === 11000) {
|
|
106
92
|
logger.debug(
|
|
107
93
|
context,
|
|
@@ -109,17 +95,12 @@ function createGroup(group, callback) {
|
|
|
109
95
|
group.resource,
|
|
110
96
|
group.apikey
|
|
111
97
|
);
|
|
112
|
-
|
|
113
98
|
callback(new errors.DuplicateGroup(group));
|
|
114
99
|
} else {
|
|
115
100
|
logger.debug(context, 'Error storing device group information: %s', error);
|
|
116
|
-
|
|
117
101
|
callback(new errors.InternalDbError(error));
|
|
118
102
|
}
|
|
119
|
-
}
|
|
120
|
-
callback(null, groupDAO.toObject());
|
|
121
|
-
}
|
|
122
|
-
});
|
|
103
|
+
});
|
|
123
104
|
}
|
|
124
105
|
|
|
125
106
|
/**
|
|
@@ -141,7 +122,7 @@ function listGroups(service, limit, offset, callback) {
|
|
|
141
122
|
}
|
|
142
123
|
|
|
143
124
|
const query = Group.model.find(condition).sort();
|
|
144
|
-
|
|
125
|
+
const queryCount = Group.model.countDocuments(condition);
|
|
145
126
|
if (limit) {
|
|
146
127
|
query.limit(parseInt(limit, 10));
|
|
147
128
|
}
|
|
@@ -149,16 +130,32 @@ function listGroups(service, limit, offset, callback) {
|
|
|
149
130
|
if (offset) {
|
|
150
131
|
query.skip(parseInt(offset, 10));
|
|
151
132
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
133
|
+
function funcQuery(cb) {
|
|
134
|
+
query
|
|
135
|
+
.exec({})
|
|
136
|
+
.then((res) => {
|
|
137
|
+
cb(null, res);
|
|
138
|
+
})
|
|
139
|
+
.catch((error) => {
|
|
140
|
+
cb(error);
|
|
159
141
|
});
|
|
160
|
-
|
|
161
|
-
)
|
|
142
|
+
}
|
|
143
|
+
function funcQueryCount(cb) {
|
|
144
|
+
queryCount
|
|
145
|
+
.exec({})
|
|
146
|
+
.then((res) => {
|
|
147
|
+
cb(null, res);
|
|
148
|
+
})
|
|
149
|
+
.catch((error) => {
|
|
150
|
+
cb(error);
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
async.series([funcQuery, funcQueryCount], function (error, results) {
|
|
154
|
+
callback(error, {
|
|
155
|
+
count: results[1],
|
|
156
|
+
services: results[0].map(toObjectFn)
|
|
157
|
+
});
|
|
158
|
+
});
|
|
162
159
|
}
|
|
163
160
|
|
|
164
161
|
function getById(id, callback) {
|
|
@@ -168,21 +165,22 @@ function getById(id, callback) {
|
|
|
168
165
|
const query = Group.model.findOne({ _id: id });
|
|
169
166
|
query.select({ __v: 0 });
|
|
170
167
|
|
|
171
|
-
query
|
|
172
|
-
|
|
168
|
+
query
|
|
169
|
+
.exec({})
|
|
170
|
+
.then((data) => {
|
|
171
|
+
if (data) {
|
|
172
|
+
context = fillService(context, data);
|
|
173
|
+
logger.debug(context, 'Device group data found: %j', data);
|
|
174
|
+
callback(null, data);
|
|
175
|
+
} else {
|
|
176
|
+
logger.debug(context, 'Device group [%s] not found.', id);
|
|
177
|
+
callback(new errors.DeviceGroupNotFound(id));
|
|
178
|
+
}
|
|
179
|
+
})
|
|
180
|
+
.catch((error) => {
|
|
173
181
|
logger.debug(context, 'Internal MongoDB Error getting group: %s', error);
|
|
174
|
-
|
|
175
182
|
callback(new errors.InternalDbError(error));
|
|
176
|
-
}
|
|
177
|
-
context = fillService(context, data);
|
|
178
|
-
logger.debug(context, 'Device group data found: %j', data);
|
|
179
|
-
callback(null, data);
|
|
180
|
-
} else {
|
|
181
|
-
logger.debug(context, 'Device group [%s] not found.', id);
|
|
182
|
-
|
|
183
|
-
callback(new errors.DeviceGroupNotFound(id));
|
|
184
|
-
}
|
|
185
|
-
});
|
|
183
|
+
});
|
|
186
184
|
}
|
|
187
185
|
|
|
188
186
|
/**
|
|
@@ -203,34 +201,56 @@ function find(service, subservice, callback) {
|
|
|
203
201
|
condition.subservice = subservice;
|
|
204
202
|
|
|
205
203
|
const query = Group.model.find(condition).sort();
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
204
|
+
const queryCount = Group.model.countDocuments(condition);
|
|
205
|
+
function funcQuery(cb) {
|
|
206
|
+
query
|
|
207
|
+
.exec({})
|
|
208
|
+
.then((res) => {
|
|
209
|
+
cb(null, res);
|
|
210
|
+
})
|
|
211
|
+
.catch((error) => {
|
|
212
|
+
cb(error);
|
|
213
213
|
});
|
|
214
|
-
|
|
215
|
-
)
|
|
214
|
+
}
|
|
215
|
+
function funcQueryCount(cb) {
|
|
216
|
+
queryCount
|
|
217
|
+
.exec({})
|
|
218
|
+
.then((res) => {
|
|
219
|
+
cb(null, res);
|
|
220
|
+
})
|
|
221
|
+
.catch((error) => {
|
|
222
|
+
cb(error);
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
async.series([funcQuery, funcQueryCount], function (error, results) {
|
|
226
|
+
callback(error, {
|
|
227
|
+
count: results[1],
|
|
228
|
+
services: results[0].map(toObjectFn)
|
|
229
|
+
});
|
|
230
|
+
});
|
|
216
231
|
}
|
|
217
232
|
|
|
218
233
|
function findOneInMongoDB(queryObj, fields, callback) {
|
|
219
234
|
const query = Group.model.findOne(queryObj);
|
|
220
235
|
query.select({ __v: 0 });
|
|
221
|
-
query.lean()
|
|
222
|
-
|
|
236
|
+
query.lean();
|
|
237
|
+
|
|
238
|
+
query
|
|
239
|
+
.exec({})
|
|
240
|
+
.then((data) => {
|
|
241
|
+
if (data) {
|
|
242
|
+
context = fillService(context, data);
|
|
243
|
+
logger.debug(context, 'Device group data found: %j', data);
|
|
244
|
+
callback(null, data);
|
|
245
|
+
} else {
|
|
246
|
+
logger.debug(context, 'Device group for fields [%j] not found: [%j]', fields, queryObj);
|
|
247
|
+
callback(new errors.DeviceGroupNotFound(fields, queryObj));
|
|
248
|
+
}
|
|
249
|
+
})
|
|
250
|
+
.catch((error) => {
|
|
223
251
|
logger.debug(context, 'Internal MongoDB Error getting group: %s', error);
|
|
224
252
|
callback(new errors.InternalDbError(error));
|
|
225
|
-
}
|
|
226
|
-
context = fillService(context, data);
|
|
227
|
-
logger.debug(context, 'Device group data found: %j', data);
|
|
228
|
-
callback(null, data);
|
|
229
|
-
} else {
|
|
230
|
-
logger.debug(context, 'Device group for fields [%j] not found: [%j]', fields, queryObj);
|
|
231
|
-
callback(new errors.DeviceGroupNotFound(fields, queryObj));
|
|
232
|
-
}
|
|
233
|
-
});
|
|
253
|
+
});
|
|
234
254
|
}
|
|
235
255
|
|
|
236
256
|
function findBy(fields) {
|
|
@@ -269,7 +289,15 @@ function update(id, body, callback) {
|
|
|
269
289
|
/* eslint-disable-next-line new-cap */
|
|
270
290
|
const groupObj = new Group.model(group);
|
|
271
291
|
groupObj.isNew = false;
|
|
272
|
-
groupObj
|
|
292
|
+
groupObj
|
|
293
|
+
.save({})
|
|
294
|
+
.then((groupDAO) => {
|
|
295
|
+
callback(null, groupDAO.toObject());
|
|
296
|
+
})
|
|
297
|
+
.catch((error) => {
|
|
298
|
+
logger.debug(fillService(context, group), 'Error storing device group information: %s', error);
|
|
299
|
+
callback(new errors.InternalDbError(error));
|
|
300
|
+
});
|
|
273
301
|
}
|
|
274
302
|
});
|
|
275
303
|
}
|
|
@@ -281,16 +309,18 @@ function remove(id, callback) {
|
|
|
281
309
|
if (error) {
|
|
282
310
|
callback(error);
|
|
283
311
|
} else {
|
|
284
|
-
Group.model.deleteOne({ _id: id }
|
|
285
|
-
|
|
286
|
-
|
|
312
|
+
const query = Group.model.deleteOne({ _id: id });
|
|
313
|
+
query
|
|
314
|
+
.exec({})
|
|
315
|
+
.then(() => {
|
|
316
|
+
logger.debug(context, 'Device group [%s] successfully removed.', id);
|
|
317
|
+
callback(null, deviceGroup);
|
|
318
|
+
})
|
|
319
|
+
.catch((error) => {
|
|
320
|
+
logger.debug(context, 'Internal MongoDB Error getting device group: %s', error);
|
|
287
321
|
|
|
288
322
|
callback(new errors.InternalDbError(error));
|
|
289
|
-
}
|
|
290
|
-
logger.debug(context, 'Device [%s] successfully removed.', id);
|
|
291
|
-
callback(null, deviceGroup);
|
|
292
|
-
}
|
|
293
|
-
});
|
|
323
|
+
});
|
|
294
324
|
}
|
|
295
325
|
});
|
|
296
326
|
}
|
|
@@ -300,7 +330,14 @@ function init(newConfig, callback) {
|
|
|
300
330
|
}
|
|
301
331
|
|
|
302
332
|
function clear(callback) {
|
|
303
|
-
|
|
333
|
+
mongoose.connection
|
|
334
|
+
.dropDatabase()
|
|
335
|
+
.then(() => {
|
|
336
|
+
callback(null);
|
|
337
|
+
})
|
|
338
|
+
.catch((error) => {
|
|
339
|
+
callback(error);
|
|
340
|
+
});
|
|
304
341
|
}
|
|
305
342
|
|
|
306
343
|
exports.create = alarmsInt(constants.MONGO_ALARM + '_01', intoTrans(context, createGroup));
|
|
@@ -347,6 +347,10 @@ function sendUpdateValueNgsi2(entityName, originMeasures, originTypeInformation,
|
|
|
347
347
|
//metadata static attributes
|
|
348
348
|
jexlctxt = reduceMetadataAttrToPlainObject(typeInformation.staticAttributes, jexlctxt);
|
|
349
349
|
|
|
350
|
+
//recover oldctxt
|
|
351
|
+
if (typeInformation.oldCtxt) {
|
|
352
|
+
jexlctxt.oldCtxt = typeInformation.oldCtxt;
|
|
353
|
+
}
|
|
350
354
|
logger.debug(
|
|
351
355
|
context,
|
|
352
356
|
'sendUpdateValueNgsi2 loop with: entityName=%s, measures=%j, typeInformation=%j, initial jexlContext=%j, timestamp=%j',
|
|
@@ -652,9 +656,15 @@ function sendUpdateValueNgsi2(entityName, originMeasures, originTypeInformation,
|
|
|
652
656
|
}
|
|
653
657
|
}
|
|
654
658
|
}
|
|
659
|
+
//Add jexlctxt to typeInformation without oldCtxt
|
|
660
|
+
const oldCJexlctxt = { ...jexlctxt };
|
|
661
|
+
delete oldCJexlctxt.oldCtxt;
|
|
662
|
+
originTypeInformation['oldCtxt'] = oldCJexlctxt;
|
|
655
663
|
} // end for (let measures of originMeasures)
|
|
656
|
-
|
|
657
664
|
let url = '/v2/op/update';
|
|
665
|
+
if (originTypeInformation.useCBflowControl) {
|
|
666
|
+
url += '?options=flowControl';
|
|
667
|
+
}
|
|
658
668
|
let options = NGSIUtils.createRequestObject(url, originTypeInformation, token);
|
|
659
669
|
options.json = payload;
|
|
660
670
|
|
|
@@ -680,6 +690,9 @@ function sendUpdateValueNgsi2(entityName, originMeasures, originTypeInformation,
|
|
|
680
690
|
if (!multi) {
|
|
681
691
|
// recreate options object to use single entity update
|
|
682
692
|
url = '/v2/entities?options=upsert';
|
|
693
|
+
if (originTypeInformation.useCBflowControl) {
|
|
694
|
+
url += ',flowControl';
|
|
695
|
+
}
|
|
683
696
|
options = NGSIUtils.createRequestObject(url, originTypeInformation, token);
|
|
684
697
|
delete payload.actionType;
|
|
685
698
|
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
const async = require('async');
|
|
28
28
|
const apply = async.apply;
|
|
29
29
|
const statsRegistry = require('../stats/statsRegistry');
|
|
30
|
+
const deviceService = require('../devices/deviceService');
|
|
30
31
|
const intoTrans = require('../common/domain').intoTrans;
|
|
31
32
|
const fillService = require('./../common/domain').fillService;
|
|
32
33
|
const errors = require('../../errors');
|
|
@@ -69,7 +70,37 @@ function init() {
|
|
|
69
70
|
*/
|
|
70
71
|
function sendUpdateValue(entityName, attributes, typeInformation, token, callback) {
|
|
71
72
|
const newCallback = statsRegistry.withStats('updateEntityRequestsOk', 'updateEntityRequestsError', callback);
|
|
72
|
-
|
|
73
|
+
const additionalCallback = (data, next) => {
|
|
74
|
+
if (typeInformation.oldCtxt) {
|
|
75
|
+
logger.debug(context, 'StoreOldCtxt %j', typeInformation.oldCtxt);
|
|
76
|
+
deviceService.storeDeviceField('oldCtxt', typeInformation.oldCtxt, typeInformation, function () {
|
|
77
|
+
next(null, data);
|
|
78
|
+
});
|
|
79
|
+
} else {
|
|
80
|
+
next(null, data);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
const wrappedNewCallback = (err, result) => {
|
|
84
|
+
if (err) {
|
|
85
|
+
newCallback(err);
|
|
86
|
+
} else {
|
|
87
|
+
additionalCallback(result, (additionalErr, modifiedResult) => {
|
|
88
|
+
if (additionalErr) {
|
|
89
|
+
newCallback(additionalErr);
|
|
90
|
+
}
|
|
91
|
+
newCallback(null, modifiedResult || result);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
// check config about store last measure
|
|
96
|
+
if (typeInformation.storeLastMeasure) {
|
|
97
|
+
logger.debug(context, 'StoreLastMeasure for %j', typeInformation);
|
|
98
|
+
deviceService.storeDeviceField('lastMeasure', attributes, typeInformation, function () {
|
|
99
|
+
return entityHandler.sendUpdateValue(entityName, attributes, typeInformation, token, wrappedNewCallback);
|
|
100
|
+
});
|
|
101
|
+
} else {
|
|
102
|
+
entityHandler.sendUpdateValue(entityName, attributes, typeInformation, token, wrappedNewCallback);
|
|
103
|
+
}
|
|
73
104
|
}
|
|
74
105
|
|
|
75
106
|
/**
|
|
@@ -292,9 +292,18 @@ function handleNotificationNgsi2(req, res, next) {
|
|
|
292
292
|
}
|
|
293
293
|
}
|
|
294
294
|
}
|
|
295
|
+
logger.debug(context, 'extracted atts %j from dataElement %j', atts, dataElement);
|
|
296
|
+
var id = null;
|
|
297
|
+
var type = null;
|
|
298
|
+
if (dataElement.targetEntityId && dataElement.targetEntityId.value) {
|
|
299
|
+
id = dataElement.targetEntityId.value;
|
|
300
|
+
}
|
|
301
|
+
if (dataElement.targetEntityType && dataElement.targetEntityType.value) {
|
|
302
|
+
type = dataElement.targetEntityType.value;
|
|
303
|
+
}
|
|
295
304
|
deviceService.getDeviceByNameAndType(
|
|
296
|
-
|
|
297
|
-
|
|
305
|
+
id,
|
|
306
|
+
type,
|
|
298
307
|
req.headers['fiware-service'],
|
|
299
308
|
req.headers['fiware-servicepath'],
|
|
300
309
|
function (error, device) {
|
|
@@ -336,7 +345,7 @@ function handleNotificationNgsi2(req, res, next) {
|
|
|
336
345
|
logger.error(context, 'Error found when processing notification: %j', error);
|
|
337
346
|
next(error);
|
|
338
347
|
} else {
|
|
339
|
-
res.status(
|
|
348
|
+
res.status(204).json();
|
|
340
349
|
}
|
|
341
350
|
}
|
|
342
351
|
|
|
@@ -30,7 +30,7 @@ const context = {
|
|
|
30
30
|
op: 'IoTAgentNGSI.ContextServer'
|
|
31
31
|
};
|
|
32
32
|
const contextServerUtils = require('./contextServerUtils');
|
|
33
|
-
|
|
33
|
+
const executeUpdateSideEffects = contextServerUtils.executeUpdateSideEffects;
|
|
34
34
|
let contextServerHandler;
|
|
35
35
|
|
|
36
36
|
/**
|
|
@@ -157,4 +157,5 @@ exports.setCommandHandler = intoTrans(context, setCommandHandler);
|
|
|
157
157
|
exports.setNotificationHandler = intoTrans(context, setNotificationHandler);
|
|
158
158
|
exports.addNotificationMiddleware = intoTrans(context, addNotificationMiddleware);
|
|
159
159
|
exports.setQueryHandler = intoTrans(context, setQueryHandler);
|
|
160
|
+
exports.executeUpdateSideEffects = intoTrans(context, executeUpdateSideEffects);
|
|
160
161
|
exports.init = init;
|
|
@@ -64,7 +64,10 @@ const provisioningAPITranslation = {
|
|
|
64
64
|
explicitAttrs: 'explicitAttrs',
|
|
65
65
|
ngsiVersion: 'ngsiVersion',
|
|
66
66
|
entityNameExp: 'entityNameExp',
|
|
67
|
-
payloadType: 'payloadType'
|
|
67
|
+
payloadType: 'payloadType',
|
|
68
|
+
useCBflowControl: 'useCBflowControl',
|
|
69
|
+
storeLastMeasure: 'storeLastMeasure',
|
|
70
|
+
lastMeasure: 'lastMeasure'
|
|
68
71
|
};
|
|
69
72
|
|
|
70
73
|
/**
|
|
@@ -143,7 +146,10 @@ function handleProvision(req, res, next) {
|
|
|
143
146
|
autoprovision: body.autoprovision,
|
|
144
147
|
explicitAttrs: body.explicitAttrs,
|
|
145
148
|
ngsiVersion: body.ngsiVersion,
|
|
146
|
-
payloadType: body.payloadType
|
|
149
|
+
payloadType: body.payloadType,
|
|
150
|
+
useCBflowControl: body.useCBflowControl,
|
|
151
|
+
storeLastMeasure: body.storeLastMeasure,
|
|
152
|
+
lastMeasure: body.lastMeasure
|
|
147
153
|
});
|
|
148
154
|
}
|
|
149
155
|
|
|
@@ -220,7 +226,10 @@ function toProvisioningAPIFormat(device) {
|
|
|
220
226
|
autoprovision: device.autoprovision,
|
|
221
227
|
explicitAttrs: device.explicitAttrs,
|
|
222
228
|
ngsiVersion: device.ngsiVersion,
|
|
223
|
-
payloadType: device.payloadType
|
|
229
|
+
payloadType: device.payloadType,
|
|
230
|
+
useCBflowControl: device.useCBflowControl,
|
|
231
|
+
storeLastMeasure: device.storeLastMeasure,
|
|
232
|
+
lastMeasure: device.lastMeasure
|
|
224
233
|
};
|
|
225
234
|
}
|
|
226
235
|
|
|
@@ -125,6 +125,7 @@ exports.setRemoveDeviceHandler = intoTrans(context, deviceProvisioning.setRemove
|
|
|
125
125
|
exports.addDeviceProvisionMiddleware = deviceProvisioning.addDeviceProvisionMiddleware;
|
|
126
126
|
exports.addConfigurationProvisionMiddleware = groupProvisioning.addConfigurationProvisionMiddleware;
|
|
127
127
|
exports.addNotificationMiddleware = contextServer.addNotificationMiddleware;
|
|
128
|
+
exports.executeUpdateSideEffects = contextServer.executeUpdateSideEffects;
|
|
128
129
|
exports.clear = clear;
|
|
129
130
|
exports.start = intoTrans(context, start);
|
|
130
131
|
exports.stop = intoTrans(context, stop);
|
|
@@ -145,10 +145,18 @@
|
|
|
145
145
|
"description": "Optional expression for command transformation",
|
|
146
146
|
"type": "string"
|
|
147
147
|
},
|
|
148
|
+
"headers": {
|
|
149
|
+
"description": "Optional headers to include with command",
|
|
150
|
+
"type": "object"
|
|
151
|
+
},
|
|
148
152
|
"payloadType": {
|
|
149
153
|
"description": "Payload type",
|
|
150
154
|
"type": "string"
|
|
151
155
|
},
|
|
156
|
+
"useCBflowControl": {
|
|
157
|
+
"description": "use CB flowControl option",
|
|
158
|
+
"type": "boolean"
|
|
159
|
+
},
|
|
152
160
|
"contentType": {
|
|
153
161
|
"description": "Content type",
|
|
154
162
|
"type": "string"
|
|
@@ -194,6 +202,14 @@
|
|
|
194
202
|
"payloadType": {
|
|
195
203
|
"description": "Payload type allowed for measures for this device",
|
|
196
204
|
"type": "string"
|
|
205
|
+
},
|
|
206
|
+
"storeLastMeasure": {
|
|
207
|
+
"description": "Store last measure",
|
|
208
|
+
"type": "boolean"
|
|
209
|
+
},
|
|
210
|
+
"lastMeasure": {
|
|
211
|
+
"description": "last measure",
|
|
212
|
+
"type": "object"
|
|
197
213
|
}
|
|
198
214
|
}
|
|
199
215
|
}
|
|
@@ -146,6 +146,18 @@
|
|
|
146
146
|
"payloadType": {
|
|
147
147
|
"description": "Payload type allowed for measures for this device",
|
|
148
148
|
"type": "string"
|
|
149
|
+
},
|
|
150
|
+
"useCBflowControl": {
|
|
151
|
+
"description": "use CB flowControl option",
|
|
152
|
+
"type": "boolean"
|
|
153
|
+
},
|
|
154
|
+
"storeLastMeasure": {
|
|
155
|
+
"description": "Store last measure",
|
|
156
|
+
"type": "boolean"
|
|
157
|
+
},
|
|
158
|
+
"lastMeasure": {
|
|
159
|
+
"description": "last measure",
|
|
160
|
+
"type": "object"
|
|
149
161
|
}
|
|
150
162
|
}
|
|
151
163
|
}
|
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.
|
|
5
|
+
"version": "4.8.0",
|
|
6
6
|
"homepage": "https://github.com/telefonicaid/iotagent-node-lib",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"fiware",
|
|
@@ -45,14 +45,14 @@
|
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"async": "2.6.4",
|
|
47
47
|
"body-parser": "~1.20.3",
|
|
48
|
-
"express": "~4.
|
|
48
|
+
"express": "~4.21.2",
|
|
49
49
|
"got": "~11.8.5",
|
|
50
50
|
"jexl": "2.3.0",
|
|
51
51
|
"jison": "0.4.18",
|
|
52
52
|
"logops": "2.1.2",
|
|
53
53
|
"moment": "~2.29.2",
|
|
54
54
|
"moment-timezone": "~0.5.34",
|
|
55
|
-
"mongoose": "
|
|
55
|
+
"mongoose": "8.9.5",
|
|
56
56
|
"query-string": "7.1.1",
|
|
57
57
|
"revalidator": "~0.3.1",
|
|
58
58
|
"underscore": "~1.13.4",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"husky": "~4.2.5",
|
|
70
70
|
"lint-staged": "~12.3.8",
|
|
71
71
|
"mocha": "10.0.0",
|
|
72
|
-
"mongodb": "4.17.
|
|
72
|
+
"mongodb": "4.17.2",
|
|
73
73
|
"nock": "13.2.7",
|
|
74
74
|
"nyc": "~15.1.0",
|
|
75
75
|
"prettier": "~2.7.1",
|
|
@@ -45,7 +45,7 @@ config.amqp = {
|
|
|
45
45
|
};
|
|
46
46
|
|
|
47
47
|
config.iota = {
|
|
48
|
-
logLevel: '
|
|
48
|
+
logLevel: 'DEBUG',
|
|
49
49
|
contextBroker: {
|
|
50
50
|
host: '192.168.1.1',
|
|
51
51
|
port: '1026',
|
|
@@ -61,7 +61,8 @@ config.iota = {
|
|
|
61
61
|
service: 'smartgondor',
|
|
62
62
|
subservice: '/gardens',
|
|
63
63
|
providerUrl: 'http://localhost:4041',
|
|
64
|
-
types: {}
|
|
64
|
+
types: {},
|
|
65
|
+
useCBflowControl: true
|
|
65
66
|
};
|
|
66
67
|
|
|
67
68
|
config.defaultKey = '1234';
|
|
@@ -74,16 +74,27 @@ function sendMeasureIotaLib(measure, provision) {
|
|
|
74
74
|
* This is not a problem for the tests using other transports than Lib, in that case, the type will be retrieved
|
|
75
75
|
* from the real provision.
|
|
76
76
|
*/
|
|
77
|
+
let typeInformation = {
|
|
78
|
+
service: provision.headers['fiware-service'],
|
|
79
|
+
subservice: provision.headers['fiware-servicepath']
|
|
80
|
+
};
|
|
77
81
|
let type;
|
|
82
|
+
let staticAttrs;
|
|
78
83
|
if (Array.isArray(provision.json.services) && provision.json.services.length > 0) {
|
|
79
84
|
type = provision.json.services[0].entity_type;
|
|
85
|
+
staticAttrs = provision.json.services[0].static_attributes;
|
|
80
86
|
} else {
|
|
81
87
|
type = DEF_TYPE;
|
|
82
88
|
}
|
|
89
|
+
typeInformation.type = type;
|
|
90
|
+
if (staticAttrs) {
|
|
91
|
+
typeInformation.staticAttributes = staticAttrs;
|
|
92
|
+
}
|
|
93
|
+
typeInformation.id = measure.qs.i;
|
|
83
94
|
iotAgentLib.update(
|
|
84
95
|
type + ':' + measure.qs.i,
|
|
85
96
|
type,
|
|
86
|
-
|
|
97
|
+
typeInformation,
|
|
87
98
|
jsonToIotaMeasures(measure.json),
|
|
88
99
|
function (error, result, body) {
|
|
89
100
|
error ? reject(error) : resolve(result);
|
|
@@ -204,9 +215,9 @@ async function testCase(measure, expectation, provision, env, config, type, tran
|
|
|
204
215
|
let cbMockRoute = '';
|
|
205
216
|
// Set the correct route depending if the test is multientity or not
|
|
206
217
|
if (type === 'multientity' || type === 'multimeasure') {
|
|
207
|
-
cbMockRoute = '/v2/op/update';
|
|
218
|
+
cbMockRoute = '/v2/op/update?options=flowControl';
|
|
208
219
|
} else {
|
|
209
|
-
cbMockRoute = '/v2/entities?options=upsert';
|
|
220
|
+
cbMockRoute = '/v2/entities?options=upsert,flowControl';
|
|
210
221
|
}
|
|
211
222
|
|
|
212
223
|
// Set the correct mock times depending if the test is multimeasure or not
|
|
@@ -230,7 +241,7 @@ async function testCase(measure, expectation, provision, env, config, type, tran
|
|
|
230
241
|
if (transport === 'MQTT') {
|
|
231
242
|
try {
|
|
232
243
|
let client = await MQTT.connectAsync('mqtt://' + config.mqtt.host);
|
|
233
|
-
await client.publish('/' + measure.qs.k + '/' + measure.qs.i + '/attrs', JSON.stringify(measure.json));
|
|
244
|
+
await client.publish('/json/' + measure.qs.k + '/' + measure.qs.i + '/attrs', JSON.stringify(measure.json));
|
|
234
245
|
await client.end();
|
|
235
246
|
} catch (error) {
|
|
236
247
|
expect.fail(ERR_MQTT + error);
|