iotagent-node-lib 2.21.0 → 2.24.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.
Files changed (62) hide show
  1. package/.nyc_output/33364de2-1199-4ec2-b33c-cae063ef8cc4.json +1 -0
  2. package/.nyc_output/processinfo/{76bc24ff-5fac-4b5a-997d-de2799342eb0.json → 33364de2-1199-4ec2-b33c-cae063ef8cc4.json} +1 -1
  3. package/.nyc_output/processinfo/index.json +1 -1
  4. package/CHANGES_NEXT_RELEASE +0 -1
  5. package/doc/Contribution.md +3 -3
  6. package/doc/advanced-topics.md +73 -9
  7. package/doc/api.md +7 -5
  8. package/doc/architecture.md +52 -5
  9. package/doc/expressionLanguage.md +18 -3
  10. package/doc/operations.md +8 -5
  11. package/doc/usermanual.md +18 -16
  12. package/docker/Mosquitto/Dockerfile +1 -1
  13. package/lib/errors.js +9 -1
  14. package/lib/model/Group.js +2 -1
  15. package/lib/model/dbConn.js +4 -0
  16. package/lib/plugins/bidirectionalData.js +104 -6
  17. package/lib/plugins/expressionPlugin.js +18 -7
  18. package/lib/plugins/multiEntity.js +42 -29
  19. package/lib/plugins/pluginUtils.js +17 -0
  20. package/lib/request-shim.js +2 -1
  21. package/lib/services/commands/commandService.js +29 -2
  22. package/lib/services/common/iotManagerService.js +2 -1
  23. package/lib/services/devices/deviceService.js +35 -9
  24. package/lib/services/groups/groupRegistryMongoDB.js +13 -12
  25. package/lib/services/ngsi/entities-NGSI-LD.js +7 -0
  26. package/lib/services/ngsi/entities-NGSI-v2.js +70 -11
  27. package/lib/services/ngsi/ngsiService.js +1 -1
  28. package/lib/services/northBound/contextServer-NGSI-LD.js +110 -15
  29. package/lib/services/northBound/contextServer-NGSI-v2.js +8 -3
  30. package/lib/services/northBound/contextServerUtils.js +9 -9
  31. package/lib/services/northBound/deviceProvisioningServer.js +2 -1
  32. package/lib/templates/createDevice.json +1 -2
  33. package/lib/templates/createDeviceLax.json +2 -3
  34. package/lib/templates/updateDevice.json +1 -2
  35. package/package.json +24 -24
  36. package/test/unit/examples/deviceProvisioningRequests/provisionMinimumDevice4.json +14 -0
  37. package/test/unit/memoryRegistry/deviceRegistryMemory_test.js +51 -0
  38. package/test/unit/mongodb/mongoDBUtils.js +2 -2
  39. package/test/unit/mongodb/mongodb-group-registry-test.js +25 -1
  40. package/test/unit/mongodb/mongodb-registry-test.js +51 -3
  41. package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerIoTAgentCommands.json +2 -1
  42. package/test/unit/ngsi-ld/examples/contextRequests/updateContextLanguageProperties1.json +15 -0
  43. package/test/unit/ngsi-ld/examples/subscriptionRequests/bidirectionalNotificationWithDatasetId.json +21 -0
  44. package/test/unit/ngsi-ld/examples/subscriptionRequests/bidirectionalNotificationWithMetadata.json +17 -0
  45. package/test/unit/ngsi-ld/lazyAndCommands/command-test.js +995 -27
  46. package/test/unit/ngsi-ld/ngsiService/languageProperties-test.js +112 -0
  47. package/test/unit/ngsi-ld/ngsiService/unsupported-endpoints-test.js +111 -0
  48. package/test/unit/ngsi-ld/plugins/bidirectional-plugin_test.js +221 -0
  49. package/test/unit/ngsi-mixed/provisioning/ngsi-versioning-test.js +1 -1
  50. package/test/unit/ngsiv2/examples/contextRequests/createMinimumProvisionedDevice4.json +8 -0
  51. package/test/unit/ngsiv2/examples/contextRequests/updateContextCommandStatus2.json +6 -0
  52. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin36.json +12 -1
  53. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin17.json +27 -0
  54. package/test/unit/ngsiv2/examples/subscriptionRequests/bidirectionalNotificationWithMetadata.json +19 -0
  55. package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +10 -8
  56. package/test/unit/ngsiv2/lazyAndCommands/command-test.js +106 -0
  57. package/test/unit/ngsiv2/lazyAndCommands/polling-commands-test.js +151 -0
  58. package/test/unit/ngsiv2/plugins/bidirectional-plugin_test.js +115 -0
  59. package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +63 -0
  60. package/test/unit/ngsiv2/provisioning/device-provisioning-api_test.js +60 -0
  61. package/test/unit/ngsiv2/provisioning/updateProvisionedDevices-test.js +2 -1
  62. package/.nyc_output/76bc24ff-5fac-4b5a-997d-de2799342eb0.json +0 -1
@@ -26,8 +26,9 @@
26
26
  const async = require('async');
27
27
  const apply = async.apply;
28
28
  const _ = require('underscore');
29
- const parser = require('./expressionParser');
29
+ const parser = require('./expressionPlugin');
30
30
  const logger = require('logops');
31
+ const config = require('../commonConfig');
31
32
  const subscriptions = require('../services/ngsi/subscriptionService');
32
33
  const deviceService = require('../services/devices/deviceService');
33
34
  const context = {
@@ -180,14 +181,17 @@ function getReverseTransformations(list, values, callback) {
180
181
  }
181
182
 
182
183
  /**
183
- * Apply a list of transformations to the reported values, generating a new array of values with the additional data.
184
+ * Apply a list of transformations to the reported values,
185
+ * generating a new array of values with the additional data.
186
+ * For NGSI-v2 this consists of value transformations only
184
187
  *
185
188
  * @param {Array} values List of reported attributes (with their name, type and value).
189
+ * @param {Object} typeInformation Provisioning information about the device represented by the entity.
186
190
  * @param {Array} transformations List of transformations to apply (with their name, type and expression).
187
191
  */
188
- function processTransformations(values, transformations, callback) {
192
+ function processTransformationsNGSIv2(values, typeInformation, transformations, callback) {
189
193
  let cleanedExpression;
190
- const ctx = parser.extractContext(values);
194
+ const ctx = parser.extractContext(values, typeInformation);
191
195
  let resultTransformations = [];
192
196
 
193
197
  for (let j = 0; j < 2; j++) {
@@ -205,13 +209,107 @@ function processTransformations(values, transformations, callback) {
205
209
  values.push({
206
210
  name: resultTransformations[i].object_id,
207
211
  type: resultTransformations[i].type,
208
- value: parser.parse(cleanedExpression, ctx, 'String')
212
+ value: parser.parse(cleanedExpression, ctx, 'String', typeInformation)
209
213
  });
210
214
  }
211
215
 
212
216
  callback(null, values);
213
217
  }
214
218
 
219
+ /**
220
+ * Apply a list of transformations to the reported values,
221
+ * generating a new array of values with the additional data.
222
+ * For NGSI-LD the output includes value, metadata and datasetId
223
+ *
224
+ * @param {Array} values List of reported attributes (with their name, type and value).
225
+ * @param {Object} typeInformation Provisioning information about the device represented by the entity.
226
+ * @param {Array} transformations List of transformations to apply (with their name, type and expression).
227
+ */
228
+ function processTransformationsNGSILD(values, typeInformation, transformations, callback) {
229
+ let cleanedExpression;
230
+ const defaultValues = [];
231
+ const datasetIds = {};
232
+
233
+ // Split incoming values into those with and without datasetId
234
+ values.forEach((value) => {
235
+ if (value.datasetId) {
236
+ datasetIds[value.datasetId] = datasetIds[value.datasetId] || [];
237
+ datasetIds[value.datasetId].push(value);
238
+ } else {
239
+ defaultValues.push(value);
240
+ }
241
+ });
242
+
243
+ let resultTransformations = [];
244
+
245
+ for (let j = 0; j < 2; j++) {
246
+ if (transformations[j]) {
247
+ resultTransformations = resultTransformations.concat(transformations[j]);
248
+ }
249
+ }
250
+
251
+ // Process Transformations using the default datasetId
252
+ // This is the direct equivalent of the NGSI-v2 function
253
+ if (!_.isEmpty(defaultValues)) {
254
+ for (let i = 0; i < resultTransformations.length; i++) {
255
+ cleanedExpression = resultTransformations[i].expression.substr(
256
+ 2,
257
+ resultTransformations[i].expression.length - 3
258
+ );
259
+ values.push({
260
+ name: resultTransformations[i].object_id,
261
+ type: resultTransformations[i].type,
262
+ metadata: {},
263
+ value: parser.parse(
264
+ cleanedExpression,
265
+ parser.extractContext(defaultValues, typeInformation),
266
+ 'String',
267
+ typeInformation
268
+ )
269
+ });
270
+ }
271
+ }
272
+
273
+ // Process Transformations using an explicit datasetId
274
+ // There is no equivalent with NGSI-v2
275
+ _.keys(datasetIds).forEach((datasetId) => {
276
+ for (let i = 0; i < resultTransformations.length; i++) {
277
+ cleanedExpression = resultTransformations[i].expression.substr(
278
+ 2,
279
+ resultTransformations[i].expression.length - 3
280
+ );
281
+ values.push({
282
+ name: resultTransformations[i].object_id,
283
+ type: resultTransformations[i].type,
284
+ datasetId,
285
+ metadata: {},
286
+ value: parser.parse(
287
+ cleanedExpression,
288
+ parser.extractContext(datasetIds[datasetId], typeInformation),
289
+ 'String',
290
+ typeInformation
291
+ )
292
+ });
293
+ }
294
+ });
295
+ callback(null, values);
296
+ }
297
+
298
+ /**
299
+ * Apply a list of transformations to the reported values, generating a new array of values with the additional data.
300
+ *
301
+ * @param {Array} values List of reported attributes (with their name, type and value).
302
+ * @param {Object} typeInformation Provisioning information about the device represented by the entity.
303
+ * @param {Array} transformations List of transformations to apply (with their name, type and expression).
304
+ */
305
+ function processTransformations(values, typeInformation, transformations, callback) {
306
+ if (config.checkNgsiLD({})) {
307
+ processTransformationsNGSILD(values, typeInformation, transformations, callback);
308
+ } else {
309
+ processTransformationsNGSIv2(values, typeInformation, transformations, callback);
310
+ }
311
+ }
312
+
215
313
  /**
216
314
  * Middleware to handle incoming Configuration group provisions. Should check for the existence of reverse active
217
315
  * attributes and create subscriptions for the modifciation of those values.
@@ -259,7 +357,7 @@ function handleNotification(device, values, callback) {
259
357
  apply(getReverseTransformations, deviceAttributes, values),
260
358
  apply(getReverseTransformations, groupAttributes, values)
261
359
  ]),
262
- apply(processTransformations, values)
360
+ apply(processTransformations, values, device)
263
361
  ],
264
362
  function (error, results) {
265
363
  callback(error, device, results);
@@ -45,21 +45,28 @@ function setJEXLTransforms(transformationMap) {
45
45
  }
46
46
 
47
47
  function applyExpression(expression, context, typeInformation) {
48
- let parser = legacyParser;
49
- if (checkJexl(typeInformation)) {
50
- parser = jexlParser;
48
+ let parser = jexlParser;
49
+ if (!checkJexl(typeInformation)) {
50
+ parser = legacyParser;
51
51
  }
52
52
  return parser.applyExpression(expression, context, typeInformation);
53
53
  }
54
54
 
55
55
  function extractContext(attributeList, typeInformation) {
56
- let parser = legacyParser;
57
- if (checkJexl(typeInformation)) {
58
- parser = jexlParser;
56
+ let parser = jexlParser;
57
+ if (!checkJexl(typeInformation)) {
58
+ parser = legacyParser;
59
59
  }
60
60
  return parser.extractContext(attributeList);
61
61
  }
62
62
 
63
+ function parse(expression, context, type, typeInformation) {
64
+ if (!checkJexl(typeInformation)) {
65
+ return legacyParser.parse(expression, context, type);
66
+ }
67
+ return jexlParser.parse(expression, context);
68
+ }
69
+
63
70
  function mergeAttributes(attrList1, attrList2) {
64
71
  const finalCollection = _.clone(attrList1);
65
72
  const additionalItems = [];
@@ -120,6 +127,8 @@ function update(entity, typeInformation, callback) {
120
127
  attributesCtxt.push(att);
121
128
  });
122
129
  }
130
+ let idTypeSSSList = utils.getIdTypeServSubServiceFromDevice(typeInformation);
131
+ attributesCtxt = attributesCtxt.concat(idTypeSSSList);
123
132
  const ctx = parser.extractContext(attributesCtxt);
124
133
 
125
134
  if (typeInformation.active) {
@@ -150,11 +159,13 @@ function update(entity, typeInformation, callback) {
150
159
 
151
160
  callback(null, entity, typeInformation);
152
161
  } catch (e) {
153
- logger.error(context, 'expressionPlugin error %j procesing entity %j', e, entity);
162
+ logger.info(context, 'expressionPlugin error %j procesing entity %j', e, entity);
154
163
  callback(e);
164
+ return;
155
165
  }
156
166
  }
157
167
 
168
+ exports.parse = parse;
158
169
  exports.update = update;
159
170
  exports.setJEXLTransforms = setJEXLTransforms;
160
171
  exports.applyExpression = applyExpression;
@@ -41,8 +41,8 @@ const utils = require('./pluginUtils');
41
41
  /* eslint-disable-next-line no-unused-vars */
42
42
  const aliasPlugin = require('./attributeAlias');
43
43
 
44
- function hasEntityName(item) {
45
- return item.entity_name;
44
+ function hasEntityNameOrEntityType(item) {
45
+ return item.entity_name || item.entity_type;
46
46
  }
47
47
 
48
48
  /**
@@ -206,37 +206,50 @@ function propagateTimestamp(entity, entities) {
206
206
  }
207
207
 
208
208
  function updateAttribute(entity, typeInformation, callback) {
209
- let parser = legacyParser;
210
- if (expressionPlugin.checkJexl(typeInformation)) {
211
- parser = jexlParser;
212
- }
213
- const attsArray = utils.extractAttributesArrayFromNgsi2Entity(entity);
214
- const ctx = parser.extractContext(attsArray);
215
-
216
- let entities = [entity];
217
- if (typeInformation.active) {
218
- const multiEntityAttributes = typeInformation.active.filter(hasEntityName);
219
- for (let i in multiEntityAttributes) {
220
- if (parser.contextAvailable(multiEntityAttributes[i].entity_name, ctx)) {
221
- let entityName = parser.applyExpression(multiEntityAttributes[i].entity_name, ctx, typeInformation);
222
- // An entity_name could not be null, but a result or expression could be null
223
- if (entityName !== null) {
224
- multiEntityAttributes[i].entity_name = entityName;
209
+ try {
210
+ logger.debug(context, 'multiEntityPlugin entity %j', entity);
211
+ let parser = jexlParser;
212
+ if (!expressionPlugin.checkJexl(typeInformation)) {
213
+ parser = legacyParser;
214
+ }
215
+ // The context for the JEXL expression should be the ID, TYPE, S, SS
216
+ let attrList = utils.getIdTypeServSubServiceFromDevice(typeInformation);
217
+ const attsArray = utils.extractAttributesArrayFromNgsi2Entity(entity);
218
+ attrList = attrList.concat(attsArray);
219
+ const ctx = parser.extractContext(attrList);
220
+ let entities = [entity];
221
+ if (typeInformation.active) {
222
+ const multiEntityAttributes = typeInformation.active.filter(hasEntityNameOrEntityType);
223
+ for (let i in multiEntityAttributes) {
224
+ if (!multiEntityAttributes[i].entity_name) {
225
+ // Then hasEntityType and entity_name should be device.name
226
+ multiEntityAttributes[i].entity_name = typeInformation.name;
227
+ }
228
+ if (parser.contextAvailable(multiEntityAttributes[i].entity_name, ctx)) {
229
+ let entityName = parser.applyExpression(multiEntityAttributes[i].entity_name, ctx, typeInformation);
230
+ // An entity_name could not be null, but a result or expression could be null
231
+ if (entityName !== null) {
232
+ multiEntityAttributes[i].entity_name = entityName;
233
+ }
225
234
  }
226
235
  }
236
+ const newEntities = extractNewEntities(multiEntityAttributes, typeInformation.type);
237
+
238
+ if (multiEntityAttributes.length > 0) {
239
+ let resultAttributes = filterOutMultientitiesNgsi2(entity, multiEntityAttributes);
240
+ const newCes = generateNewCEsNgsi2(entity, newEntities, multiEntityAttributes);
241
+ entities = [resultAttributes].concat(newCes);
242
+ propagateTimestamp(entity, entities);
243
+ } else {
244
+ entities = entity;
245
+ }
227
246
  }
228
- const newEntities = extractNewEntities(multiEntityAttributes, typeInformation.type);
229
-
230
- if (multiEntityAttributes.length > 0) {
231
- let resultAttributes = filterOutMultientitiesNgsi2(entity, multiEntityAttributes);
232
- const newCes = generateNewCEsNgsi2(entity, newEntities, multiEntityAttributes);
233
- entities = [resultAttributes].concat(newCes);
234
- propagateTimestamp(entity, entities);
235
- } else {
236
- entities = entity;
237
- }
247
+ callback(null, entities, typeInformation);
248
+ } catch (e) {
249
+ logger.info(context, 'multiEntityPlugin error %j procesing entity %j', e, entity);
250
+ callback(e);
251
+ return;
238
252
  }
239
- callback(null, entities, typeInformation);
240
253
  }
241
254
 
242
255
  exports.update = updateAttribute;
@@ -167,9 +167,26 @@ function identityFilter(entity, typeInformation, callback) {
167
167
  callback(null, entity, typeInformation);
168
168
  }
169
169
 
170
+ /**
171
+ * Creates an array of attributes from an device including id, type, service and subservice
172
+ * @param {Object} device
173
+ * @return {Object} Array of id, type, service and subservice extracted from device
174
+ */
175
+ function getIdTypeServSubServiceFromDevice(typeInformation) {
176
+ let attrList = [
177
+ { name: 'id', type: 'String', value: typeInformation.id },
178
+ { name: 'type', type: 'String', value: typeInformation.type },
179
+ { name: 'service', type: 'String', value: typeInformation.service },
180
+ { name: 'subservice', type: 'String', value: typeInformation.subservice },
181
+ { name: 'entity_name', type: 'String', value: typeInformation.name }
182
+ ];
183
+ return attrList;
184
+ }
185
+
170
186
  exports.createProcessAttribute = createProcessAttribute;
171
187
  exports.createUpdateFilter = createUpdateFilter;
172
188
  exports.createQueryFilter = createQueryFilter;
173
189
  exports.identityFilter = identityFilter;
174
190
  exports.createNgsi2Entity = createNgsi2Entity;
175
191
  exports.extractAttributesArrayFromNgsi2Entity = extractAttributesArrayFromNgsi2Entity;
192
+ exports.getIdTypeServSubServiceFromDevice = getIdTypeServSubServiceFromDevice;
@@ -26,6 +26,7 @@ const logger = require('logops');
26
26
  const context = {
27
27
  op: 'IoTAgentNGSI.Request'
28
28
  };
29
+ const util = require('util');
29
30
 
30
31
  /**
31
32
  * Transform the "request" options into "got" options and add additional "got" defaults
@@ -103,7 +104,7 @@ function request(options, callback) {
103
104
  return callback(null, response, response.body);
104
105
  })
105
106
  .catch((error) => {
106
- logger.debug(context, 'Error: %s', JSON.stringify(error, null, 4));
107
+ logger.debug(context, 'Error: %s', JSON.stringify(util.inspect(error), null, 4));
107
108
  return callback(error);
108
109
  });
109
110
  }
@@ -29,10 +29,12 @@ const config = require('../../commonConfig');
29
29
  const constants = require('../../constants');
30
30
  const ngsiService = require('../ngsi/ngsiService');
31
31
  const deviceService = require('../devices/deviceService');
32
+ const pluginUtils = require('../../plugins/pluginUtils');
32
33
  let daemonId;
33
34
  const context = {
34
35
  op: 'IoTAgentNGSI.CommandService'
35
36
  };
37
+ const expressionPlugin = require('../../plugins/expressionPlugin');
36
38
 
37
39
  function listCommands(service, subservice, deviceId, callback) {
38
40
  logger.debug(context, 'Listing all commands for device [%s]', deviceId);
@@ -41,11 +43,35 @@ function listCommands(service, subservice, deviceId, callback) {
41
43
  }
42
44
 
43
45
  function addCommand(service, subservice, deviceId, command, callback) {
44
- logger.debug(context, 'Adding command [%s] to the queue for device [%s]', command.name, deviceId);
45
-
46
+ logger.debug(context, 'Adding command [%j] to the queue for device [%s]', command, deviceId);
46
47
  config.getCommandRegistry().add(service, subservice, deviceId, command, callback);
47
48
  }
48
49
 
50
+ function addCommandDevice(service, subservice, device, command, callback) {
51
+ logger.debug(context, 'Adding command [%j] to the queue for device [%j]', command, device);
52
+ let deviceCmd;
53
+ if (device && device.commands) {
54
+ deviceCmd = device.commands.find((c) => c.name === command.name);
55
+ }
56
+ if (deviceCmd && deviceCmd.expression) {
57
+ let parser = expressionPlugin;
58
+ // The context for the JEXL expression should be the ID, TYPE, S, SS
59
+ let attrList = pluginUtils.getIdTypeServSubServiceFromDevice(device);
60
+ attrList = device.staticAttributes ? attrList.concat(device.staticAttributes) : attrList.concat([]);
61
+ let ctxt = parser.extractContext(attrList, device);
62
+ logger.debug(context, 'attrList [%j] for device %j', attrList, device);
63
+ // expression result will be the full command payload
64
+ let cmdValueRes = null;
65
+ try {
66
+ cmdValueRes = parser.applyExpression(deviceCmd.expression, ctxt, device);
67
+ } catch (e) {
68
+ // nothing to do
69
+ }
70
+ command.value = cmdValueRes ? cmdValueRes : deviceCmd.expression;
71
+ }
72
+ config.getCommandRegistry().add(service, subservice, device.id, command, callback);
73
+ }
74
+
49
75
  function updateCommand(service, subservice, deviceId, name, value, callback) {
50
76
  logger.debug(context, 'Updating command [%s] for device [%s] with value [%s]', name, deviceId, value);
51
77
 
@@ -147,6 +173,7 @@ function stopExpirationDaemon(callback) {
147
173
 
148
174
  exports.list = intoTrans(context, listCommands);
149
175
  exports.add = addCommand;
176
+ exports.addCmd = addCommandDevice;
150
177
  exports.update = updateCommand;
151
178
  exports.remove = removeCommand;
152
179
  exports.start = startExpirationDaemon;
@@ -60,7 +60,8 @@ function register(callback) {
60
60
  explicitAttrs: service.explicitAttrs,
61
61
  expressionLanguage: service.expressionLanguage,
62
62
  defaultEntityNameConjunction: service.defaultEntityNameConjunction,
63
- ngsiVersion: service.ngsiVersion
63
+ ngsiVersion: service.ngsiVersion,
64
+ entityNameExp: service.entityNameExp
64
65
  };
65
66
  }
66
67
 
@@ -38,6 +38,8 @@ const logger = require('logops');
38
38
  const config = require('../../commonConfig');
39
39
  const registrationUtils = require('./registrationUtils');
40
40
  const subscriptions = require('../ngsi/subscriptionService');
41
+ const expressionPlugin = require('./../../plugins/expressionPlugin');
42
+ const pluginUtils = require('./../../plugins/pluginUtils');
41
43
  const _ = require('underscore');
42
44
  const context = {
43
45
  op: 'IoTAgentNGSI.DeviceService'
@@ -280,15 +282,45 @@ function registerDevice(deviceObj, callback) {
280
282
  }
281
283
 
282
284
  if (!deviceData.name) {
285
+ let entityName = null;
286
+ if (configuration && configuration.entityNameExp !== undefined && configuration.entityNameExp !== '') {
287
+ // Add device ID, TYPE, S, SS to the context for the JEXL expression
288
+ let attrList = pluginUtils.getIdTypeServSubServiceFromDevice(deviceData);
289
+ attrList = deviceData.staticAttributes ? attrList.concat(deviceData.staticAttributes) : attrList;
290
+ let ctxt = expressionPlugin.extractContext(attrList, deviceData);
291
+ try {
292
+ entityName = expressionPlugin.applyExpression(configuration.entityNameExp, ctxt, deviceData);
293
+ } catch (e) {
294
+ logger.debug(
295
+ context,
296
+ 'Error evaluating expression for entityName: %s with context: %s',
297
+ configuration.entityNameExp,
298
+ ctxt
299
+ );
300
+ }
301
+ }
302
+ deviceData.name = entityName ? entityName : defaultName();
303
+ }
304
+
305
+ if (!configuration && config.getConfig().types[deviceData.type]) {
306
+ selectedConfiguration = config.getConfig().types[deviceData.type];
307
+ } else {
308
+ selectedConfiguration = configuration;
309
+ }
310
+ callback(null, deviceData, selectedConfiguration);
311
+
312
+ function defaultName() {
283
313
  let conjunction;
314
+ let name;
284
315
  if (configuration && configuration.defaultEntityNameConjunction !== undefined) {
285
316
  conjunction = configuration.defaultEntityNameConjunction;
286
317
  } else {
287
318
  conjunction = config.getConfig().defaultEntityNameConjunction;
288
319
  }
289
- deviceData.name = deviceData.type + conjunction + deviceData.id;
320
+ name = deviceData.type + conjunction + deviceData.id;
321
+
290
322
  if (config.checkNgsiLD(configuration)) {
291
- deviceData.name = 'urn:ngsi-ld:' + deviceData.type + conjunction + deviceData.id;
323
+ name = 'urn:ngsi-ld:' + deviceData.type + conjunction + deviceData.id;
292
324
  }
293
325
  logger.debug(
294
326
  context,
@@ -296,14 +328,8 @@ function registerDevice(deviceObj, callback) {
296
328
  conjunction,
297
329
  deviceData.name
298
330
  );
331
+ return name;
299
332
  }
300
-
301
- if (!configuration && config.getConfig().types[deviceData.type]) {
302
- selectedConfiguration = config.getConfig().types[deviceData.type];
303
- } else {
304
- selectedConfiguration = configuration;
305
- }
306
- callback(null, deviceData, selectedConfiguration);
307
333
  }
308
334
 
309
335
  function completeRegistrations(error, deviceData) {
@@ -57,7 +57,8 @@ const attributeList = [
57
57
  'explicitAttrs',
58
58
  'expressionLanguage',
59
59
  'defaultEntityNameConjunction',
60
- 'ngsiVersion'
60
+ 'ngsiVersion',
61
+ 'entityNameExp'
61
62
  ];
62
63
 
63
64
  /**
@@ -299,21 +300,21 @@ function clear(callback) {
299
300
  dbService.db.db.dropDatabase(callback);
300
301
  }
301
302
 
302
- exports.create = alarmsInt(constants.MONGO_ALARM, intoTrans(context, createGroup));
303
- exports.list = alarmsInt(constants.MONGO_ALARM, intoTrans(context, listGroups));
304
- exports.init = alarmsInt(constants.MONGO_ALARM, intoTrans(context, init));
305
- exports.find = alarmsInt(constants.MONGO_ALARM, intoTrans(context, find));
303
+ exports.create = alarmsInt(constants.MONGO_ALARM + '_01', intoTrans(context, createGroup));
304
+ exports.list = alarmsInt(constants.MONGO_ALARM + '_02', intoTrans(context, listGroups));
305
+ exports.init = alarmsInt(constants.MONGO_ALARM + '_03', intoTrans(context, init));
306
+ exports.find = alarmsInt(constants.MONGO_ALARM + '_04', intoTrans(context, find));
306
307
  exports.findType = alarmsInt(
307
- constants.MONGO_ALARM,
308
+ constants.MONGO_ALARM + '_05',
308
309
  intoTrans(context, findBy(['service', 'subservice', 'type', 'apikey']))
309
310
  );
310
311
  exports.findTypeSilently = intoTrans(context, findBy(['service', 'subservice', 'type', 'apikey']));
311
312
  exports.findSilently = intoTrans(context, findBy(['service', 'subservice', 'apikey']));
312
- exports.findBy = alarmsInt(constants.MONGO_ALARM, intoTrans(context, findBy));
313
- exports.get = alarmsInt(constants.MONGO_ALARM, intoTrans(context, findBy(['resource', 'apikey'])));
313
+ exports.findBy = alarmsInt(constants.MONGO_ALARM + '_06', intoTrans(context, findBy));
314
+ exports.get = alarmsInt(constants.MONGO_ALARM + '_07', intoTrans(context, findBy(['resource', 'apikey'])));
314
315
  exports.getSilently = intoTrans(context, findBy(['resource', 'apikey']));
315
- exports.getType = alarmsInt(constants.MONGO_ALARM, intoTrans(context, findBy(['type'])));
316
+ exports.getType = alarmsInt(constants.MONGO_ALARM + '_08', intoTrans(context, findBy(['type'])));
316
317
  exports.getTypeSilently = intoTrans(context, findBy(['type']));
317
- exports.update = alarmsInt(constants.MONGO_ALARM, intoTrans(context, update));
318
- exports.remove = alarmsInt(constants.MONGO_ALARM, intoTrans(context, remove));
319
- exports.clear = alarmsInt(constants.MONGO_ALARM, intoTrans(context, clear));
318
+ exports.update = alarmsInt(constants.MONGO_ALARM + '_09', intoTrans(context, update));
319
+ exports.remove = alarmsInt(constants.MONGO_ALARM + '_10', intoTrans(context, remove));
320
+ exports.clear = alarmsInt(constants.MONGO_ALARM + '_11', intoTrans(context, clear));
@@ -160,6 +160,13 @@ function convertAttrNGSILD(attr) {
160
160
  delete obj.value;
161
161
  break;
162
162
 
163
+ // LanguageProperties
164
+ case 'languageproperty':
165
+ obj.type = 'LanguageProperty';
166
+ obj.languageMap = attr.value;
167
+ delete obj.value;
168
+ break;
169
+
163
170
  default:
164
171
  obj.value = { '@type': attr.type, '@value': attr.value };
165
172
  }