Skip to content

Commit b00a739

Browse files
committed
Merge pull request #68 from telefonicaid/develop
New release 0.3.0
2 parents 9c217cf + 3ef82dc commit b00a739

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+3645
-250
lines changed

CHANGES_NEXT_RELEASE

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1-
- Complete the Provisioning API with CRUD Operations.
2-
- Change signature of unregister() funciton to remove unneeded type.
3-
- Support XML in Context Provider updates and queries.
4-
- Fix multiple errors found during preparation of CPBR8 Workshop.
5-
- Improve the documentation to add: Configuration, Security, and IoT Library testing sections.
6-
- Fix package.json information to prepare the package for publishing.
1+
- ADD Keywords to the package.json.
2+
- ADD Configuration API for dynamically creating configuration groups.
3+
- ADD Device Provisioning API and Device Configuration API commands in the testing clients.
4+
- ADD Pagination in the Device List operations.
5+
- FIX Remove mandatory constraint for service, servicepath and commands in Device Provisioning (#46).
6+
- FIX List devices based on the service and servicepath (#45).
7+
- FIX Lazy attributes mandatory in Device Provisioning (#51).
8+
- FIX Should allow to provision devices with unconfigured type (#50)
9+
- ADD Handler for configuration changes from the Configuration API.
10+
- ADD Constraint to forbid multiple devices with the same ID (#48).
11+
- ADD Constraint to ensure uniqueness of device configurations (#53).

README.md

Lines changed: 199 additions & 27 deletions
Large diffs are not rendered by default.

bin/iotAgentTester.js

Lines changed: 222 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ var config = require('../config'),
3535
service: 'tester',
3636
subservice: '/test'
3737
},
38+
configIot = {
39+
host: 'localhost',
40+
port: 4041,
41+
name: 'default',
42+
service: 'tester',
43+
subservice: '/test'
44+
},
3845
separator = '\n\n\t';
3946

4047
function queryContext(commands) {
@@ -177,6 +184,27 @@ function configure(commands) {
177184
config.subservice = commands[3];
178185
}
179186

187+
function showConfig(commands) {
188+
console.log('\nCurrent configuration:\n\n');
189+
console.log(JSON.stringify(config, null, 4));
190+
console.log('\n');
191+
clUtils.prompt();
192+
}
193+
194+
function configureIot(commands) {
195+
configIot.host = commands[0];
196+
configIot.port = commands[1];
197+
config.service = commands[2];
198+
config.subservice = commands[3];
199+
}
200+
201+
function showConfigIot(commands) {
202+
console.log('\nCurrent configuration:\n\n');
203+
console.log(JSON.stringify(configIot, null, 4));
204+
console.log('\n');
205+
clUtils.prompt();
206+
}
207+
180208
function discoverContext(commands) {
181209
var options = {
182210
url: 'http://' + config.host + ':' + config.port + '/v1/registry/discoverContextAvailability',
@@ -212,7 +240,7 @@ function discoverContext(commands) {
212240
function provisionDevice(commands) {
213241
function generateOptions(deviceConfig, callback) {
214242
var options = {
215-
uri: 'http://' + commands[0] + ':' + commands[1] + '/iot/devices',
243+
uri: 'http://' + configIot.host + ':' + configIot.port + '/iot/devices',
216244
method: 'POST'
217245
};
218246

@@ -246,7 +274,7 @@ function provisionDevice(commands) {
246274
clUtils.prompt();
247275
}
248276

249-
fs.readFile(commands[2], 'utf8', function (error, deviceConfig) {
277+
fs.readFile(commands[0], 'utf8', function (error, deviceConfig) {
250278
if (error && error.code === 'ENOENT') {
251279
console.error('File not found');
252280
clUtils.prompt();
@@ -259,11 +287,157 @@ function provisionDevice(commands) {
259287
});
260288
}
261289

262-
function showConfig(commands) {
263-
console.log('\nCurrent configuration:\n\n');
264-
console.log(JSON.stringify(config, null, 4));
265-
console.log('\n');
266-
clUtils.prompt();
290+
function listProvisioned(commands) {
291+
var options = {
292+
uri: 'http://' + configIot.host + ':' + configIot.port + '/iot/devices',
293+
method: 'GET'
294+
};
295+
296+
console.log('Devices provisioned in host [%s:%s]', configIot.host, configIot.port);
297+
console.log('----------------------------------------------------------------');
298+
299+
request(options, function(error, result, body) {
300+
if (error) {
301+
console.log('Couldn\'t connect with the provisioning server: ' + error.toString());
302+
} else if (result.statusCode === 200 && body) {
303+
var parsedBody = JSON.parse(body);
304+
console.log(JSON.stringify(parsedBody, null, 4));
305+
} else {
306+
console.log('Unexpected application error. Status: ' + result.statusCode);
307+
}
308+
clUtils.prompt();
309+
});
310+
}
311+
312+
function removeProvisioned(commands) {
313+
var options = {
314+
uri: 'http://' + configIot.host + ':' + configIot.port + '/iot/devices/' + commands[0],
315+
method: 'DELETE'
316+
};
317+
318+
console.log('Removing device [%s] [%s:%s]', commands[0], configIot.host, configIot.port);
319+
console.log('----------------------------------------------------------------');
320+
321+
request(options, function(error, result, body) {
322+
if (error) {
323+
console.log('Couldn\'t connect with the provisioning server: ' + error.toString());
324+
} else if (result.statusCode === 200 && body) {
325+
var parsedBody = JSON.parse(body);
326+
console.log('Device [%s] removed successfully', commands[0]);
327+
} else {
328+
console.log('Unexpected application error. Status: ' + result.statusCode);
329+
}
330+
clUtils.prompt();
331+
});
332+
}
333+
334+
function addGroup(commands) {
335+
console.log('Adding device groups to host [%s:%s] from file [%s]', configIot.host, configIot.port, commands[0]);
336+
337+
function generateOptions(deviceConfig, callback) {
338+
var options = {
339+
uri: 'http://' + configIot.host + ':' + configIot.port + '/iot/agents/' + configIot.name + '/services',
340+
method: 'POST',
341+
headers: {
342+
'fiware-service': configIot.service,
343+
'fiware-servicepath': configIot.subservice
344+
}
345+
};
346+
347+
try {
348+
var payload = JSON.parse(deviceConfig);
349+
options.json = payload;
350+
callback(null, options);
351+
} catch (e) {
352+
callback('Wrong JSON. Couldn\'t parse');
353+
}
354+
}
355+
356+
function sendProvisionRequest(options, callback) {
357+
request(options, function(error, result, body) {
358+
if (error) {
359+
callback('Couldn\'t connect with the provisioning server: ' + error.toString());
360+
} else if (result.statusCode === 200 && body) {
361+
callback(null, 'Device group successfully provisioned');
362+
} else {
363+
callback('Unexpected application error. Status: ' + result.statusCode);
364+
}
365+
});
366+
}
367+
368+
function handleOut(error, msg) {
369+
if (error) {
370+
console.error(error);
371+
} else {
372+
console.log(msg);
373+
}
374+
clUtils.prompt();
375+
}
376+
377+
fs.readFile(commands[0], 'utf8', function (error, deviceConfig) {
378+
if (error && error.code === 'ENOENT') {
379+
console.error('File not found');
380+
clUtils.prompt();
381+
} else {
382+
async.waterfall([
383+
async.apply(generateOptions, deviceConfig),
384+
sendProvisionRequest
385+
], handleOut);
386+
}
387+
});
388+
}
389+
390+
function removeGroup(commands) {
391+
var options = {
392+
uri: 'http://' + configIot.host + ':' + configIot.port + '/iot/agents/' + configIot.name + '/services',
393+
method: 'DELETE',
394+
headers: {
395+
'fiware-service': configIot.service,
396+
'fiware-servicepath': configIot.subservice
397+
}
398+
};
399+
400+
console.log('Removing device group for subservice [%s] from [%s:%s]',
401+
configIot.subservice, configIot.host, configIot.port);
402+
403+
console.log('----------------------------------------------------------------');
404+
405+
request(options, function(error, result, body) {
406+
if (error) {
407+
console.log('Couldn\'t connect with the provisioning server: ' + error.toString());
408+
} else if (result.statusCode === 200 && body) {
409+
console.log('Device group for subservice [%s] removed successfully', configIot.subservice);
410+
} else {
411+
console.log('Unexpected application error. Status: ' + result.statusCode);
412+
}
413+
clUtils.prompt();
414+
});
415+
}
416+
417+
function listGroups(commands) {
418+
console.log('Devices groups provisioned in host [%s:%s]', configIot.host, configIot.port);
419+
console.log('----------------------------------------------------------------');
420+
var options = {
421+
uri: 'http://' + configIot.host + ':' + configIot.port + '/iot/agents/' + configIot.name + '/services',
422+
method: 'GET',
423+
headers: {
424+
'fiware-service': configIot.service,
425+
'fiware-servicepath': '/*'
426+
}
427+
};
428+
429+
request(options, function(error, result, body) {
430+
console.log(JSON.stringify(result.headers));
431+
if (error) {
432+
console.log('Couldn\'t connect with the provisioning server: ' + error.toString());
433+
} else if (result.statusCode === 200 && body) {
434+
var parsedBody = JSON.parse(body);
435+
console.log(JSON.stringify(parsedBody, null, 4));
436+
} else {
437+
console.log('Unexpected application error. Status: ' + result.statusCode);
438+
}
439+
clUtils.prompt();
440+
});
267441
}
268442

269443
var commands = {
@@ -294,21 +468,57 @@ var commands = {
294468
description: '\tGet all the context providers for a entity and type.',
295469
handler: discoverContext
296470
},
297-
'config': {
471+
'configCb': {
298472
parameters: ['host', 'port', 'service', 'subservice'],
299473
description: '\tConfig a new host and port for the remote Context Broker.',
300474
handler: configure
301475
},
302-
'showConfig': {
476+
'showConfigCb': {
303477
parameters: [],
304-
description: '\tShow the current configuration of the client.',
478+
description: '\tShow the current configuration of the client for the Context Broker.',
305479
handler: showConfig
306480
},
481+
'configIot': {
482+
parameters: ['host', 'port', 'service', 'subservice'],
483+
description: '\tConfig a new host and port for the remote IoT Agent.',
484+
handler: configureIot
485+
},
486+
'showConfigIot': {
487+
parameters: [],
488+
description: '\tShow the current configuration of the client for the IoT Agent.',
489+
handler: showConfigIot
490+
},
307491
'provision': {
308-
parameters: ['host', 'port', 'filename'],
492+
parameters: ['filename'],
309493
description: '\tProvision a new device using the Device Provisioning API. The device configuration is \n' +
310-
'\tread from the script location.',
494+
'\tread from the file specified in the "filename" parameter.',
311495
handler: provisionDevice
496+
},
497+
'listProvisioned': {
498+
parameters: [],
499+
description: '\tList all the provisioned devices in an IoT Agent.',
500+
handler: listProvisioned
501+
},
502+
'removeProvisioned': {
503+
parameters: ['deviceId'],
504+
description: '\tRemove the selected provisioned device from the IoT Agent, specified by its Device ID.',
505+
handler: removeProvisioned
506+
},
507+
'addGroup': {
508+
parameters: ['filename'],
509+
description: '\tAdd a new device group to the specified IoT Agent through the Configuration API. The \n' +
510+
'\tbody is taken from the file specified in the "filename" parameter.',
511+
handler: addGroup
512+
},
513+
'listGroups': {
514+
parameters: [],
515+
description: '\tList all the device groups created in the selected IoT Agent for the configured service',
516+
handler: listGroups
517+
},
518+
'removeGroup': {
519+
parameters: [],
520+
description: '\tRemove the device group corresponding to the current configured subservice.',
521+
handler: removeGroup
312522
}
313523
};
314524

config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ var config = {
6666
service: 'smartGondor',
6767
subservice: '/gardens',
6868
providerUrl: 'http://192.168.56.1:4041',
69-
deviceRegistrationDuration: 'P1M'
69+
deviceRegistrationDuration: 'P1M',
70+
defaultType: 'Thing'
7071
};
7172

7273
module.exports = config;

examples/deviceGroup.json

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"services": [
3+
{
4+
"resource": "/deviceTest",
5+
"apikey": "801230BJKL23Y9090DSFL123HJK09H324HV8732",
6+
"type": "Light",
7+
"trust": "8970A9078A803H3BL98PINEQRW8342HBAMS",
8+
"cbHost": "http://unexistentHost:1026",
9+
"commands": [
10+
{
11+
"name": "wheel1",
12+
"type": "Wheel"
13+
}
14+
],
15+
"lazy": [
16+
{
17+
"name": "luminescence",
18+
"type": "Lumens"
19+
}
20+
],
21+
"active": [
22+
{
23+
"name": "status",
24+
"type": "Boolean"
25+
}
26+
]
27+
}
28+
]
29+
}

lib/errors.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ module.exports = {
6161
TypeNotFound: function(id, type) {
6262
this.name = 'TYPE_NOT_FOUND';
6363
this.message = 'Type : ' + type + ' not found for device with id: ' + id;
64+
this.code = 500;
6465
},
6566
MissingAttributes: function(msg) {
6667
this.name = 'MISSING_ATTRIBUTES';
@@ -71,6 +72,16 @@ module.exports = {
7172
this.message = 'No device was found with id:' + id;
7273
this.code = 404;
7374
},
75+
DuplicateDeviceId: function(id) {
76+
this.name = 'DUPLICATE_DEVICE_ID';
77+
this.message = 'A device with the same pair (Service, DeviceId) was found:' + id;
78+
this.code = 400;
79+
},
80+
DuplicateGroup: function(res, key) {
81+
this.name = 'DUPLICATE_GROUP';
82+
this.message = 'A device configuration already exists for resource ' + res + ' and API Key ' + key;
83+
this.code = 400;
84+
},
7485
SecurityInformationMissing: function(type) {
7586
this.name = 'SECURITY_INFORMATION_MISSING';
7687
this.message = 'Some security information was missing for device type:' + type;
@@ -95,5 +106,30 @@ module.exports = {
95106
TemplateLoadingError: function(error) {
96107
this.name = 'TEMPLATE_LOADING_ERROR';
97108
this.message = 'Some of the XML Templates could not be found or loaded: ' + error.toString();
109+
},
110+
MissingHeaders: function(msg) {
111+
this.name = 'MISSING_HEADERS';
112+
this.message = 'Some headers were missing from the request: ' + msg;
113+
this.code = 400;
114+
},
115+
MismatchedService: function(service, subservice) {
116+
this.name = 'MISMATCHED_SERVICE';
117+
this.message = 'The declared service didn\'t match the stored one in the entity';
118+
this.code = 403;
119+
},
120+
WrongSyntax: function(msg) {
121+
this.name = 'WRONG_SYNTAX';
122+
this.message = 'Wrong syntax in request: ' + msg;
123+
this.code = 400;
124+
},
125+
DeviceGroupNotFound: function(fields, values) {
126+
this.name = 'DEVICE_GROUP_NOT_FOUND';
127+
if (values && fields) {
128+
this.message = 'Couldn\t find device group for fields: ' + fields + ' and values: ' + values;
129+
} else {
130+
this.message = 'Couldn\t find device group';
131+
}
132+
this.code = 400;
98133
}
134+
99135
};

0 commit comments

Comments
 (0)