iotagent-node-lib 4.7.0 → 4.9.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 (110) hide show
  1. package/.github/workflows/ci.yml +6 -3
  2. package/.nyc_output/e4b1fe20-197e-4221-9f8d-6db65ff234b2.json +1 -0
  3. package/.nyc_output/processinfo/e4b1fe20-197e-4221-9f8d-6db65ff234b2.json +1 -0
  4. package/.nyc_output/processinfo/index.json +1 -0
  5. package/CHANGES_NEXT_RELEASE +1 -0
  6. package/Changelog +793 -0
  7. package/doc/README.md +1 -0
  8. package/doc/admin.md +8 -4
  9. package/doc/api.md +8 -4
  10. package/doc/devel/northboundinteractions.md +122 -15
  11. package/doc/models/models.md +260 -0
  12. package/doc/requirements.txt +1 -1
  13. package/docker/Mosquitto/Dockerfile +1 -1
  14. package/lib/commonConfig.js +11 -2
  15. package/lib/fiware-iotagent-lib.js +15 -11
  16. package/lib/jexlTranformsMap.js +182 -35
  17. package/lib/model/Command.js +9 -0
  18. package/lib/model/Device.js +1 -0
  19. package/lib/services/commands/commandRegistryMongoDB.js +15 -1
  20. package/lib/services/commands/commandService.js +3 -3
  21. package/lib/services/common/domain.js +4 -3
  22. package/lib/services/devices/deviceRegistryMongoDB.js +1 -1
  23. package/lib/services/devices/deviceService.js +11 -2
  24. package/lib/services/devices/devices-NGSI-LD.js +1 -2
  25. package/lib/services/devices/devices-NGSI-v2.js +1 -2
  26. package/lib/services/ngsi/entities-NGSI-LD.js +69 -13
  27. package/lib/services/ngsi/ngsiService.js +3 -1
  28. package/lib/services/northBound/contextServer-NGSI-LD.js +5 -3
  29. package/lib/services/northBound/contextServer-NGSI-v2.js +12 -3
  30. package/lib/services/northBound/contextServer.js +2 -1
  31. package/lib/services/northBound/northboundServer.js +2 -1
  32. package/lib/services/northBound/restUtils.js +6 -2
  33. package/lib/templates/createDevice.json +4 -0
  34. package/lib/templates/updateDevice.json +4 -0
  35. package/package.json +1 -1
  36. package/test/functional/config-test.js +1 -1
  37. package/test/functional/testUtils.js +13 -2
  38. package/test/unit/expressions/jexlExpression-test.js +165 -1
  39. package/test/unit/ngsi-ld/examples/contextRequests/createDatetimeProvisionedDevice.json +1 -4
  40. package/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDevice.json +3 -12
  41. package/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDeviceMultientity.json +3 -12
  42. package/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDeviceWithGroupAndStatic.json +6 -24
  43. package/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDeviceWithGroupAndStatic2.json +1 -4
  44. package/test/unit/ngsi-ld/examples/contextRequests/updateContext1.json +1 -4
  45. package/test/unit/ngsi-ld/examples/contextRequests/updateContext3WithStatic.json +1 -4
  46. package/test/unit/ngsi-ld/examples/contextRequests/updateContext4.json +1 -4
  47. package/test/unit/ngsi-ld/examples/contextRequests/updateContext5.json +1 -4
  48. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAliasPlugin6.json +1 -4
  49. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAliasPlugin7.json +1 -4
  50. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAliasPlugin8.json +4 -2
  51. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAliasPlugin9.json +1 -4
  52. package/test/unit/ngsi-ld/examples/contextRequests/updateContextCommandError.json +3 -9
  53. package/test/unit/ngsi-ld/examples/contextRequests/updateContextCommandExpired.json +13 -19
  54. package/test/unit/ngsi-ld/examples/contextRequests/updateContextCommandFinish.json +13 -19
  55. package/test/unit/ngsi-ld/examples/contextRequests/updateContextCommandStatus1.json +4 -9
  56. package/test/unit/ngsi-ld/examples/contextRequests/updateContextCompressTimestamp1.json +1 -4
  57. package/test/unit/ngsi-ld/examples/contextRequests/updateContextCompressTimestamp2.json +1 -4
  58. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin1a.json +1 -4
  59. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin2.json +2 -8
  60. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin29.json +1 -4
  61. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin4.json +2 -8
  62. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin4a.json +3 -12
  63. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin6.json +3 -5
  64. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin7.json +1 -4
  65. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin8a.json +1 -4
  66. package/test/unit/ngsi-ld/examples/contextRequests/updateContextJsonProperty.json +13 -0
  67. package/test/unit/ngsi-ld/examples/contextRequests/updateContextListProperty.json +14 -0
  68. package/test/unit/ngsi-ld/examples/contextRequests/updateContextListRelationship.json +14 -0
  69. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin1.json +2 -8
  70. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin2.json +2 -8
  71. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin3.json +3 -8
  72. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin4.json +1 -4
  73. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin5.json +2 -8
  74. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin8.json +2 -8
  75. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityTimestampPlugin1.json +2 -8
  76. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityTimestampPlugin2.json +1 -4
  77. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityTimestampPlugin3.json +1 -4
  78. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityTimestampPlugin4.json +2 -8
  79. package/test/unit/ngsi-ld/examples/contextRequests/updateContextRelationship.json +11 -0
  80. package/test/unit/ngsi-ld/examples/contextRequests/updateContextValueType1.json +51 -0
  81. package/test/unit/ngsi-ld/examples/contextRequests/updateContextValueType2.json +65 -0
  82. package/test/unit/ngsi-ld/examples/contextRequests/updateContextVocabProperty.json +11 -0
  83. package/test/unit/ngsi-ld/examples/contextRequests/updateProvisionCommands1.json +2 -8
  84. package/test/unit/ngsi-ld/examples/contextRequests/updateProvisionDeviceStatic.json +1 -4
  85. package/test/unit/ngsi-ld/general/startup-test.js +3 -0
  86. package/test/unit/ngsi-ld/lazyAndCommands/command-test.js +1 -1
  87. package/test/unit/ngsi-ld/lazyAndCommands/lazy-devices-test.js +71 -1
  88. package/test/unit/ngsi-ld/ngsiService/attributeTypes-test.js +293 -0
  89. package/test/unit/ngsi-ld/ngsiService/unsupported-endpoints-test.js +0 -10
  90. package/test/unit/ngsi-ld/ngsiService/value-types-test.js +221 -0
  91. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast1.json +0 -11
  92. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast10.json +0 -14
  93. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast2.json +0 -11
  94. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast3.json +0 -11
  95. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast4.json +0 -11
  96. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast5.json +0 -7
  97. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast6.json +0 -17
  98. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast7.json +0 -19
  99. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast8.json +0 -14
  100. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast9.json +0 -14
  101. package/test/unit/ngsi-ld/ngsiService/languageProperties-test.js +0 -109
  102. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast1.json +0 -8
  103. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast2.json +0 -8
  104. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast3.json +0 -8
  105. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast4.json +0 -8
  106. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast5.json +0 -8
  107. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast6.json +0 -8
  108. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast7.json +0 -8
  109. package/test/unit/ngsiv2/ngsiService/subscriptions-test.js +0 -326
  110. /package/test/unit/ngsi-ld/examples/contextRequests/{updateContextLanguageProperties1.json → updateContextLanguageProperty.json} +0 -0
@@ -26,63 +26,210 @@
26
26
  JEXL avaliable transformations*/
27
27
 
28
28
  const map = {
29
- jsonparse: (str) => JSON.parse(str),
30
- jsonstringify: (obj) => JSON.stringify(obj),
29
+ jsonparse: (val) => {
30
+ const safeOperation =
31
+ (fn) =>
32
+ (...args) => {
33
+ try {
34
+ return fn(...args);
35
+ } catch (e) {
36
+ return null;
37
+ }
38
+ };
39
+ return safeOperation(JSON.parse)(val);
40
+ },
41
+ jsonstringify: (val) => {
42
+ const safeOperation =
43
+ (fn) =>
44
+ (...args) => {
45
+ try {
46
+ return fn(...args);
47
+ } catch (e) {
48
+ return null;
49
+ }
50
+ };
51
+ return safeOperation(JSON.stringify)(val);
52
+ },
31
53
  indexOf: (val, char) => String(val).indexOf(char),
32
54
  length: (val) => String(val).length,
33
55
  trim: (val) => String(val).trim(),
34
56
  substr: (val, int1, int2) => String(val).substr(int1, int2),
35
- addreduce: (arr) => arr.reduce((i, v) => i + v),
57
+ addreduce: (arr) => {
58
+ const safeOperation =
59
+ (fn) =>
60
+ (...args) => {
61
+ try {
62
+ return fn(...args);
63
+ } catch (e) {
64
+ return null;
65
+ }
66
+ };
67
+ return safeOperation((arr) => arr.reduce((i, v) => i + v))(arr);
68
+ },
36
69
  lengtharray: (arr) => arr.length,
37
70
  typeof: (val) => typeof val,
38
- isarray: (arr) => Array.isArray(arr),
39
- isnan: (val) => isNaN(val),
40
- parseint: (val) => parseInt(val),
41
- parsefloat: (val) => parseFloat(val),
42
- toisodate: (val) => new Date(val).toISOString(),
43
- timeoffset: (isostr) => new Date(isostr).getTimezoneOffset(),
44
- tostring: (val) => val.toString(),
45
- urlencode: (val) => encodeURI(val),
46
- urldecode: (val) => decodeURI(val),
71
+ isarray: Array.isArray,
72
+ isnan: isNaN,
73
+ parseint: (val) => {
74
+ const safeParseNumber = (fn) => (val) => {
75
+ const result = fn(val);
76
+ return isNaN(result) ? null : result;
77
+ };
78
+ return safeParseNumber((val) => parseInt(val, 10))(val);
79
+ },
80
+ parsefloat: (val) => {
81
+ const safeParseNumber = (fn) => (val) => {
82
+ const result = fn(val);
83
+ return isNaN(result) ? null : result;
84
+ };
85
+ return safeParseNumber(parseFloat)(val);
86
+ },
87
+ toisodate: (val) => {
88
+ const safeDateOperation = (fn) => (val) => {
89
+ const date = new Date(val);
90
+ return isNaN(date.getTime()) ? null : fn(date);
91
+ };
92
+ return safeDateOperation((date) => date.toISOString())(val);
93
+ },
94
+ timeoffset: (val) => {
95
+ const safeDateOperation = (fn) => (val) => {
96
+ const date = new Date(val);
97
+ return isNaN(date.getTime()) ? null : fn(date);
98
+ };
99
+ return safeDateOperation((date) => date.getTimezoneOffset())(val);
100
+ },
101
+ tostring: (val) => {
102
+ const safeOperation =
103
+ (fn) =>
104
+ (...args) => {
105
+ try {
106
+ return fn(...args);
107
+ } catch (e) {
108
+ return null;
109
+ }
110
+ };
111
+ return safeOperation((val) => val.toString())(val);
112
+ },
113
+ urlencode: encodeURI,
114
+ urldecode: (val) => {
115
+ const safeOperation =
116
+ (fn) =>
117
+ (...args) => {
118
+ try {
119
+ return fn(...args);
120
+ } catch (e) {
121
+ return null;
122
+ }
123
+ };
124
+ return safeOperation(decodeURI)(val);
125
+ },
47
126
  replacestr: (str, from, to) => str.replace(from, to),
48
- replaceregexp: (str, reg, to) => str.replace(new RegExp(reg), to),
49
- replaceallstr: (str, from, to) => str.replaceAll(from, to),
50
- replaceallregexp: (str, reg, to) => str.replaceAll(new RegExp(reg, 'g'), to),
127
+ replaceregexp: (str, reg, to) => {
128
+ const safeOperation =
129
+ (fn) =>
130
+ (...args) => {
131
+ try {
132
+ return fn(...args);
133
+ } catch (e) {
134
+ return null;
135
+ }
136
+ };
137
+ return safeOperation((str, reg, to) => str.replace(new RegExp(reg), to))(str, reg, to);
138
+ },
139
+ replaceallregexp: (str, reg, to) => {
140
+ const safeOperation =
141
+ (fn) =>
142
+ (...args) => {
143
+ try {
144
+ return fn(...args);
145
+ } catch (e) {
146
+ return null;
147
+ }
148
+ };
149
+ return safeOperation((str, reg, to) => str.replace(new RegExp(reg, 'g'), to))(str, reg, to);
150
+ },
51
151
  split: (str, ch) => str.split(ch),
52
152
  joinarrtostr: (arr, ch) => arr.join(ch),
53
153
  concatarr: (arr, arr2) => arr.concat(arr2),
54
154
  mapper: (val, values, choices) => choices[values.findIndex((target) => target === val)],
55
155
  thmapper: (val, values, choices) =>
56
- choices[
57
- values.reduce((acc, curr, i) => (acc === 0 || acc ? acc : val <= curr ? (acc = i) : (acc = null)), null)
58
- ],
156
+ choices[values.reduce((acc, curr, i) => (acc !== null ? acc : val <= curr ? i : null), null)],
59
157
  bitwisemask: (i, mask, op, shf) =>
60
158
  (op === '&' ? parseInt(i) & mask : op === '|' ? parseInt(i) | mask : op === '^' ? parseInt(i) ^ mask : i) >>
61
159
  shf,
62
160
  slice: (arr, init, end) => arr.slice(init, end),
63
- addset: (arr, x) => {
64
- return Array.from(new Set(arr).add(x));
65
- },
161
+ addset: (arr, x) => Array.from(new Set(arr).add(x)),
66
162
  removeset: (arr, x) => {
67
- let s = new Set(arr);
163
+ const s = new Set(arr);
68
164
  s.delete(x);
69
165
  return Array.from(s);
70
166
  },
71
167
  touppercase: (val) => String(val).toUpperCase(),
72
168
  tolowercase: (val) => String(val).toLowerCase(),
73
- floor: (val) => Math.floor(val),
74
- ceil: (val) => Math.ceil(val),
75
- round: (val) => Math.round(val),
76
- tofixed: (val, decimals) => Number.parseFloat(val).toFixed(decimals),
77
- gettime: (d) => new Date(d).getTime(),
78
- toisostring: (d) => new Date(d).toISOString(),
79
- // https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString
80
- localestring: (d, timezone, options) => new Date(d).toLocaleString(timezone, options),
81
- now: () => Date.now(),
82
- hextostring: (val) =>
83
- new TextDecoder().decode(new Uint8Array(val.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)))),
84
- valuePicker: (val,pick) => Object.entries(val).filter(([, v]) => v === pick).map(([k,]) => k),
85
- valuePickerMulti: (val,pick) => Object.entries(val).filter(([, v]) => pick.includes(v)).map(([k,]) => k)
169
+ floor: Math.floor,
170
+ ceil: Math.ceil,
171
+ round: Math.round,
172
+ tofixed: (val, decimals) => {
173
+ const num = Number.parseFloat(val);
174
+ const dec = Number.parseInt(decimals);
175
+ return isNaN(num) || isNaN(dec) ? null : num.toFixed(dec);
176
+ },
177
+ gettime: (val) => {
178
+ const safeDateOperation = (fn) => (val) => {
179
+ const date = new Date(val);
180
+ return isNaN(date.getTime()) ? null : fn(date);
181
+ };
182
+ return safeDateOperation((date) => date.getTime())(val);
183
+ },
184
+ toisostring: (val) => {
185
+ const safeDateOperation = (fn) => (val) => {
186
+ const date = new Date(val);
187
+ return isNaN(date.getTime()) ? null : fn(date);
188
+ };
189
+ return safeDateOperation((date) => date.toISOString())(val);
190
+ },
191
+ localestring: (d, timezone, options) => {
192
+ const safeOperation =
193
+ (fn) =>
194
+ (...args) => {
195
+ try {
196
+ return fn(...args);
197
+ } catch (e) {
198
+ return null;
199
+ }
200
+ };
201
+ return safeOperation((d, timezone, options) => new Date(d).toLocaleString(timezone, options))(
202
+ d,
203
+ timezone,
204
+ options
205
+ );
206
+ },
207
+ now: Date.now,
208
+ hextostring: (val) => {
209
+ const safeOperation =
210
+ (fn) =>
211
+ (...args) => {
212
+ try {
213
+ return fn(...args);
214
+ } catch (e) {
215
+ return null;
216
+ }
217
+ };
218
+ return safeOperation((val) => {
219
+ if (typeof val !== 'string' || !/^[0-9a-fA-F]+$/.test(val) || val.length % 2 !== 0) {
220
+ return null;
221
+ }
222
+ return new TextDecoder().decode(new Uint8Array(val.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))));
223
+ })(val);
224
+ },
225
+ valuePicker: (val, pick) =>
226
+ Object.entries(val)
227
+ .filter(([, v]) => v === pick)
228
+ .map(([k]) => k),
229
+ valuePickerMulti: (val, pick) =>
230
+ Object.entries(val)
231
+ .filter(([, v]) => pick.includes(v))
232
+ .map(([k]) => k)
86
233
  };
87
234
 
88
235
  exports.map = map;
@@ -31,6 +31,15 @@ const Command = new Schema({
31
31
  value: Object,
32
32
  service: { type: String, lowercase: true },
33
33
  subservice: String,
34
+ execTs: { type: Date },
35
+ status: String,
36
+ info: String,
37
+ onDelivered: Object,
38
+ onOk: Object,
39
+ onError: Object,
40
+ onInfo: Object,
41
+ cmdExecution: Boolean,
42
+ dateExpiration: { type: Date },
34
43
  creationDate: { type: Date, default: Date.now }
35
44
  });
36
45
 
@@ -61,6 +61,7 @@ const Device = new Schema({
61
61
  });
62
62
 
63
63
  function load() {
64
+ Device.index({ service: 1, subservice: 1, apikey: 1, id: 1 }, { unique: true });
64
65
  module.exports.model = mongoose.model('Device', Device);
65
66
  module.exports.internalSchema = Device;
66
67
  }
@@ -89,7 +89,21 @@ function updateCommand(service, subservice, deviceId, command, callback) {
89
89
  function createCommand(service, subservice, deviceId, command, callback) {
90
90
  /* eslint-disable-next-line new-cap */
91
91
  const commandObj = new Command.model();
92
- const attributeList = ['name', 'type', 'value'];
92
+ const attributeList = [
93
+ 'name',
94
+ 'type',
95
+ 'value',
96
+ // new Command fields
97
+ 'execTs',
98
+ 'status',
99
+ 'info',
100
+ 'onDelivered',
101
+ 'onOk',
102
+ 'onError',
103
+ 'onInfo',
104
+ 'cmdExecution',
105
+ 'dateExpiration'
106
+ ];
93
107
 
94
108
  for (let i = 0; i < attributeList.length; i++) {
95
109
  commandObj[attributeList[i]] = command[attributeList[i]];
@@ -43,7 +43,7 @@ function listCommands(service, subservice, deviceId, callback) {
43
43
  }
44
44
 
45
45
  function addCommand(service, subservice, deviceId, command, callback) {
46
- logger.debug(context, 'Adding command [%j] to the queue for device [%s]', command, deviceId);
46
+ logger.debug(context, 'Adding command [%j] to the queue for deviceId [%s]', command, deviceId);
47
47
  config.getCommandRegistry().add(service, subservice, deviceId, command, callback);
48
48
  }
49
49
 
@@ -73,13 +73,13 @@ function addCommandDevice(service, subservice, device, command, callback) {
73
73
  }
74
74
 
75
75
  function updateCommand(service, subservice, deviceId, name, value, callback) {
76
- logger.debug(context, 'Updating command [%s] for device [%s] with value [%s]', name, deviceId, value);
76
+ logger.debug(context, 'Updating command [%s] for deviceId [%s] with value [%s]', name, deviceId, value);
77
77
 
78
78
  config.getCommandRegistry().update(service, subservice, deviceId, value, callback);
79
79
  }
80
80
 
81
81
  function removeCommand(service, subservice, deviceId, name, callback) {
82
- logger.debug(context, 'Removing command [%s] from device [%s]', name, deviceId);
82
+ logger.debug(context, 'Removing command [%s] from deviceId [%s]', name, deviceId);
83
83
 
84
84
  config.getCommandRegistry().remove(service, subservice, deviceId, name, callback);
85
85
  }
@@ -68,7 +68,7 @@ function requestDomain(req, res, next) {
68
68
  reqDomain.path = req.path;
69
69
  reqDomain.op = req.url;
70
70
  reqDomain.start = Date.now();
71
-
71
+ reqDomain.from = req.ip || req.connection.remoteAddress;
72
72
  reqDomain.add(req);
73
73
  reqDomain.add(res);
74
74
 
@@ -145,14 +145,15 @@ function ensureSouthboundTransaction(context, callback) {
145
145
  if (context.op) {
146
146
  reqDomain.op = context.op;
147
147
  }
148
-
149
148
  if (context.srv) {
150
149
  reqDomain.service = context.srv;
151
150
  }
152
-
153
151
  if (context.subsrv) {
154
152
  reqDomain.subservice = context.subsrv;
155
153
  }
154
+ if (context.from) {
155
+ reqDomain.from = context.from;
156
+ }
156
157
  }
157
158
 
158
159
  return reqDomain.run(callback);
@@ -90,7 +90,7 @@ function storeDevice(newDevice, callback) {
90
90
  if (error.code === 11000) {
91
91
  logger.debug(context, 'Tried to insert a device with duplicate ID in the database: %s', error);
92
92
 
93
- callback(new errors.DuplicateDeviceId(newDevice));
93
+ callback(new errors.DuplicateDeviceId(newDevice), newDevice);
94
94
  } else {
95
95
  logger.debug(context, 'Error storing device information: %s', error);
96
96
 
@@ -40,6 +40,8 @@ const registrationUtils = require('./registrationUtils');
40
40
  const subscriptions = require('../ngsi/subscriptionService');
41
41
  const expressionPlugin = require('./../../plugins/expressionPlugin');
42
42
  const pluginUtils = require('./../../plugins/pluginUtils');
43
+ const alarms = require('../common/alarmManagement');
44
+ const constants = require('../../constants');
43
45
  const _ = require('underscore');
44
46
  const context = {
45
47
  op: 'IoTAgentNGSI.DeviceService'
@@ -250,7 +252,7 @@ function registerDevice(deviceObj, callback) {
250
252
  /* eslint-disable-next-line no-unused-vars */
251
253
  function (error, device) {
252
254
  if (!error) {
253
- innerCb(new errors.DuplicateDeviceId(deviceObj));
255
+ innerCb(new errors.DuplicateDeviceId(deviceObj), device);
254
256
  } else {
255
257
  innerCb();
256
258
  }
@@ -664,7 +666,14 @@ function findOrCreate(deviceId, apikey, group, callback) {
664
666
  if (group.autoprovision === undefined || group.autoprovision === true) {
665
667
  logger.debug(context, 'Registering autoprovision of Device %j for its conf %j', newDevice, group);
666
668
  registerDevice(newDevice, function (error, device) {
667
- callback(error, device, group);
669
+ if (error && error.name === 'DUPLICATE_DEVICE_ID') {
670
+ alarms.release(constants.MONGO_ALARM, error);
671
+ logger.warn(context, 'Error %j already registered autoprovisioned device: %j ', error, device);
672
+ callback(null, device, group);
673
+ } else {
674
+ logger.debug(context, 'registered autoprovisioned device: %j ', device);
675
+ callback(error, device, group);
676
+ }
668
677
  });
669
678
  } else {
670
679
  logger.info(
@@ -33,7 +33,6 @@ const logger = require('logops');
33
33
  const config = require('../../commonConfig');
34
34
  const ngsiLD = require('../ngsi/entities-NGSI-LD');
35
35
  const utils = require('../northBound/restUtils');
36
- const moment = require('moment');
37
36
  const _ = require('underscore');
38
37
  const registrationUtils = require('./registrationUtils');
39
38
  const NGSIv2 = require('./devices-NGSI-v2');
@@ -153,7 +152,7 @@ function updateEntityNgsiLD(deviceData, updatedDevice, callback) {
153
152
  ) {
154
153
  options.json[constants.TIMESTAMP_ATTRIBUTE] = {
155
154
  type: constants.TIMESTAMP_TYPE_NGSI2,
156
- value: moment()
155
+ value: new Date().toISOString()
157
156
  };
158
157
  }
159
158
 
@@ -41,7 +41,6 @@ const registrationUtils = require('./registrationUtils');
41
41
  const _ = require('underscore');
42
42
  const utils = require('../northBound/restUtils');
43
43
  const NGSIv2 = require('../ngsi/entities-NGSI-v2');
44
- const moment = require('moment');
45
44
  const context = {
46
45
  op: 'IoTAgentNGSI.Devices-v2'
47
46
  };
@@ -222,7 +221,7 @@ function updateEntityNgsi2(deviceData, updatedDevice, callback) {
222
221
  ) {
223
222
  options.json[constants.TIMESTAMP_ATTRIBUTE] = {
224
223
  type: constants.TIMESTAMP_TYPE_NGSI2,
225
- value: moment()
224
+ value: new Date().toISOString()
226
225
  };
227
226
  }
228
227
 
@@ -59,10 +59,13 @@ function convertAttrNGSILD(attr) {
59
59
  return undefined;
60
60
  }
61
61
  let obj = { type: 'Property', value: attr.value };
62
+ let hasValueType = true;
62
63
 
63
64
  switch (attr.type.toLowerCase()) {
64
65
  // Properties
65
66
  case 'property':
67
+ hasValueType = false;
68
+ break;
66
69
  case 'string':
67
70
  case 'text':
68
71
  case 'textunrestricted':
@@ -96,24 +99,26 @@ function convertAttrNGSILD(attr) {
96
99
  }
97
100
  break;
98
101
 
102
+ case 'object':
103
+ case 'array':
104
+ try {
105
+ obj.value = JSON.parse(attr.value);
106
+ } catch (e) {
107
+ // Do nothing
108
+ }
109
+ break;
110
+
99
111
  // Temporal Properties
100
112
  case 'datetime':
101
- obj.value = {
102
- '@type': 'DateTime',
103
- '@value': moment.tz(attr.value, 'Etc/UTC').toISOString()
104
- };
113
+ obj.value = moment.tz(attr.value, 'Etc/UTC').toISOString();
105
114
  break;
106
115
  case 'date':
107
- obj.value = {
108
- '@type': 'Date',
109
- '@value': moment.tz(attr.value, 'Etc/UTC').format(moment.HTML5_FMT.DATE)
110
- };
116
+ obj.value = moment.tz(attr.value, 'Etc/UTC').format(moment.HTML5_FMT.DATE);
111
117
  break;
112
118
  case 'time':
113
- obj.value = {
114
- '@type': 'Time',
115
- '@value': moment.tz(attr.value, 'Etc/UTC').format(moment.HTML5_FMT.TIME_SECONDS)
116
- };
119
+ obj.value = moment
120
+ .tz(new Date('0000-01-01 ' + attr.value), 'Etc/UTC')
121
+ .format(moment.HTML5_FMT.TIME_SECONDS);
117
122
  break;
118
123
 
119
124
  // GeoProperties
@@ -121,37 +126,44 @@ function convertAttrNGSILD(attr) {
121
126
  case 'point':
122
127
  case 'geo:point':
123
128
  case 'geo:json':
129
+ hasValueType = false;
124
130
  obj.type = 'GeoProperty';
125
131
  obj.value = NGSIUtils.getLngLats('Point', attr.value);
126
132
  break;
127
133
  case 'linestring':
128
134
  case 'geo:linestring':
135
+ hasValueType = false;
129
136
  obj.type = 'GeoProperty';
130
137
  obj.value = NGSIUtils.getLngLats('LineString', attr.value);
131
138
  break;
132
139
  case 'polygon':
133
140
  case 'geo:polygon':
141
+ hasValueType = false;
134
142
  obj.type = 'GeoProperty';
135
143
  obj.value = NGSIUtils.getLngLats('Polygon', attr.value);
136
144
  break;
137
145
  case 'multipoint':
138
146
  case 'geo:multipoint':
147
+ hasValueType = false;
139
148
  obj.type = 'GeoProperty';
140
149
  obj.value = NGSIUtils.getLngLats('MultiPoint', attr.value);
141
150
  break;
142
151
  case 'multilinestring':
143
152
  case 'geo:multilinestring':
153
+ hasValueType = false;
144
154
  obj.type = 'GeoProperty';
145
155
  obj.value = NGSIUtils.getLngLats('MultiLineString', attr.value);
146
156
  break;
147
157
  case 'multipolygon':
148
158
  case 'geo:multipolygon':
159
+ hasValueType = false;
149
160
  obj.type = 'GeoProperty';
150
161
  obj.value = NGSIUtils.getLngLats('MultiPolygon', attr.value);
151
162
  break;
152
163
 
153
164
  // Relationships
154
165
  case 'relationship':
166
+ hasValueType = false;
155
167
  obj.type = 'Relationship';
156
168
  obj.object = attr.value;
157
169
  delete obj.value;
@@ -159,13 +171,57 @@ function convertAttrNGSILD(attr) {
159
171
 
160
172
  // LanguageProperties
161
173
  case 'languageproperty':
174
+ hasValueType = false;
162
175
  obj.type = 'LanguageProperty';
163
176
  obj.languageMap = attr.value;
164
177
  delete obj.value;
165
178
  break;
166
179
 
180
+ // VocabProperties
181
+ case 'vocabproperty':
182
+ hasValueType = false;
183
+ obj.type = 'VocabProperty';
184
+ obj.vocab = attr.value;
185
+ delete obj.value;
186
+ break;
187
+ // JsonProperties
188
+ case 'jsonproperty':
189
+ hasValueType = false;
190
+ obj.type = 'JsonProperty';
191
+ obj.json = attr.value;
192
+ delete obj.value;
193
+ break;
194
+ // ListProperties
195
+ case 'listproperty':
196
+ hasValueType = false;
197
+ obj.type = 'ListProperty';
198
+ obj.listValue = attr.value;
199
+ delete obj.value;
200
+ break;
201
+ // ListRelationship
202
+ case 'listrelationship':
203
+ hasValueType = false;
204
+ obj.type = 'ListRelationship';
205
+ obj.listObject = attr.value;
206
+ delete obj.value;
207
+ break;
208
+
167
209
  default:
168
- obj.value = { '@type': attr.type, '@value': attr.value };
210
+ obj.value = attr.value;
211
+ }
212
+
213
+ if (hasValueType) {
214
+ switch (config.getConfig().server.ldSupport.dataType) {
215
+ case '@type':
216
+ obj.value = {
217
+ '@type': attr.type,
218
+ '@value': obj.value
219
+ };
220
+ break;
221
+ case 'valueType':
222
+ obj.valueType = attr.type;
223
+ break;
224
+ }
169
225
  }
170
226
 
171
227
  if (!!obj && attr.metadata) {
@@ -95,7 +95,9 @@ function sendUpdateValue(entityName, attributes, typeInformation, token, callbac
95
95
  // check config about store last measure
96
96
  if (typeInformation.storeLastMeasure) {
97
97
  logger.debug(context, 'StoreLastMeasure for %j', typeInformation);
98
- deviceService.storeDeviceField('lastMeasure', attributes, typeInformation, function () {
98
+ let originalMeasure = typeInformation.originalMeasure ? typeInformation.originalMeasure : null;
99
+ deviceService.storeDeviceField('lastMeasure', originalMeasure, typeInformation, function () {
100
+ delete typeInformation.originalMeasure;
99
101
  return entityHandler.sendUpdateValue(entityName, attributes, typeInformation, token, wrappedNewCallback);
100
102
  });
101
103
  } else {
@@ -46,7 +46,7 @@ const config = require('../../commonConfig');
46
46
 
47
47
  const overwritePaths = ['/ngsi-ld/v1/entities/:entity/attrs', '/ngsi-ld/v1/entities/:entity/attrs/:attr'];
48
48
  const updatePaths = ['/ngsi-ld/v1/entities/:entity/attrs', '/ngsi-ld/v1/entities/:entity/attrs/:attr'];
49
- const queryPaths = ['/ngsi-ld/v1/entities/:entity'];
49
+ const queryPaths = ['/ngsi-ld/v1/entities/:entity', '/ngsi-ld/v1/entities'];
50
50
 
51
51
  /**
52
52
  * Replacement of NGSI-LD Null placeholders with real null values
@@ -506,6 +506,9 @@ function handleMergePatchNgsiLD(req, res, next) {
506
506
  * @param {Object} res Response that will be sent.
507
507
  */
508
508
  function handleQueryNgsiLD(req, res, next) {
509
+ const returnAsArray = req.query.id;
510
+ req.params.entity = req.params.entity || req.query.id || '';
511
+
509
512
  function getName(element) {
510
513
  return element.name;
511
514
  }
@@ -641,7 +644,7 @@ function handleQueryNgsiLD(req, res, next) {
641
644
  next(error);
642
645
  } else {
643
646
  logger.debug(context, 'Query from [%s] handled successfully.', req.get('host'));
644
- res.status(200).json(result);
647
+ res.status(200).json(returnAsArray ? [result] : result);
645
648
  }
646
649
  }
647
650
 
@@ -793,7 +796,6 @@ function loadUnsupportedEndpointsNGSILD(router) {
793
796
  const unsupportedEndpoint = function (req, res) {
794
797
  return res.status(501).send(new errors.MethodNotSupported(req.method, req.path));
795
798
  };
796
- router.get('/ngsi-ld/v1/entities', unsupportedEndpoint);
797
799
  router.post('/ngsi-ld/v1/entities', unsupportedEndpoint);
798
800
  router.delete('/ngsi-ld/v1/entities/:entity', unsupportedEndpoint);
799
801
  router.delete('/ngsi-ld/v1/entities/:entity/attrs/:attr', unsupportedEndpoint);
@@ -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
- dataElement.id,
297
- dataElement.type,
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(200).json({});
348
+ res.status(204).json();
340
349
  }
341
350
  }
342
351