Skip to content

Commit

Permalink
Merge 5ac886e into 632f391
Browse files Browse the repository at this point in the history
  • Loading branch information
busticated committed Apr 27, 2020
2 parents 632f391 + 5ac886e commit 8603007
Show file tree
Hide file tree
Showing 8 changed files with 257 additions and 40 deletions.
110 changes: 74 additions & 36 deletions npm-shrinkwrap.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@
"sinon-chai": "^3.3.0"
},
"optionalDependencies": {
"particle-usb": "^0.5.4",
"particle-usb": "^1.1.0",
"serialport": "^8.0.5"
},
"engines": {
Expand Down
28 changes: 28 additions & 0 deletions src/cli/usb.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,34 @@ module.exports = ({ commandProcessor, root }) => {
}
});

commandProcessor.createCommand(usb, 'cloud-status', 'Check a device\'s cloud connection state', {
params: '<device>',
options: {
'until': {
description: 'Poll your device for a specific connection state and then exit',
choices: [
'unknown',
'disconnected',
'connecting',
'connected',
'disconnecting'
]
},
'timeout': {
description: 'How long should polling wait (in ms) for the requested status?',
number: true,
default: 1 * 60 * 1000
}
},
examples: {
'$0 $command blue': 'Check the cloud connection status for the device named `blue`',
'$0 $command red --until connected': 'Poll cloud connection status for the device named `red` until it reports `connected`'
},
handler: (args) => {
return usbCommand().cloudStatus(args);
}
});

return usb;
};

60 changes: 60 additions & 0 deletions src/cli/usb.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ describe('USB Command-Line Interface', () => {
' reset Reset a device',
' setup-done Set the setup done flag',
' configure Update the system USB configuration',
' cloud-status Check a device\'s cloud connection state',
''
].join(os.EOL));
});
Expand Down Expand Up @@ -338,5 +339,64 @@ describe('USB Command-Line Interface', () => {
});
});
});

describe('Handles `usb cloud-status` Command', () => {
it('Parses arguments', () => {
const argv = commandProcessor.parse(root, ['usb', 'cloud-status', 'my-device']);
expect(argv.clierror).to.equal(undefined);
expect(argv.params).to.eql({ device: 'my-device' });
expect(argv.until).to.equal(undefined);
expect(argv.timeout).to.equal(60000);
});

it('Errors when required `device` argument is missing', () => {
const argv = commandProcessor.parse(root, ['usb', 'cloud-status']);
expect(argv.clierror).to.be.an.instanceof(Error);
expect(argv.clierror).to.have.property('message', 'Parameter \'device\' is required.');
expect(argv.clierror).to.have.property('data', 'device');
expect(argv.clierror).to.have.property('isUsageError', true);
expect(argv.params).to.eql({});
expect(argv.until).to.equal(undefined);
expect(argv.timeout).to.equal(60000);
});

it('Parses options flags', () => {
const argv = commandProcessor.parse(root, ['usb', 'cloud-status', 'my-device', '--until', 'disconnected', '--timeout', '2000']);
expect(argv.clierror).to.equal(undefined);
expect(argv.params).to.eql({ device: 'my-device' });
expect(argv.until).to.equal('disconnected');
expect(argv.timeout).to.equal(2000);
});

it('Errors when invalid option value is provided', () => {
const argv = commandProcessor.parse(root, ['usb', 'cloud-status', 'my-device', '--until', 'NOPE']);
// TODO (mirande): should this be an error?
expect(argv.clierror).to.not.be.an.instanceof(Error);
expect(argv.clierror).to.include('Invalid values:');
expect(argv.clierror).to.include('Argument: until, Given: "NOPE", Choices: "unknown", "disconnected", "connecting", "connected", "disconnecting"');
expect(argv.params).to.eql({ device: 'my-device' });
expect(argv.until).to.equal('NOPE');
expect(argv.timeout).to.equal(60000);
});

it('Includes help with examples', () => {
commandProcessor.parse(root, ['usb', 'cloud-status', '--help'], termWidth);
commandProcessor.showHelp((helpText) => {
expect(helpText).to.equal([
'Check a device\'s cloud connection state',
'Usage: particle usb cloud-status [options] <device>',
'',
'Options:',
' --until Poll your device for a specific connection state and then exit [string] [choices: "unknown", "disconnected", "connecting", "connected", "disconnecting"]',
' --timeout How long should polling wait (in ms) for the requested status? [number] [default: 60000]',
'',
'Examples:',
' particle usb cloud-status blue Check the cloud connection status for the device named `blue`',
' particle usb cloud-status red --until connected Poll cloud connection status for the device named `red` until it reports `connected`',
''
].join(os.EOL));
});
});
});
});

1 change: 1 addition & 0 deletions src/cmd/usb-util.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ function openUsbDevice(usbDevice, { dfuMode = false } = {}){
* @param {String} [options.displayName] Device name as shown to the user.
* @return {Promise}
*/
// TODO (mirande): name is confusing since it handles opening by device id OR name
function openUsbDeviceById({ id, api, auth, dfuMode = false, displayName = null }){
return Promise.resolve()
.then(() => {
Expand Down
48 changes: 48 additions & 0 deletions src/cmd/usb.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,54 @@ module.exports = class UsbCommand {
.then(() => console.log('Done.'));
}

cloudStatus(args, started = Date.now()){
const { until, timeout, params: { device } } = args;

if (Date.now() - (started + timeout) > 0){
throw new Error('timed-out waiting for status...');
}

const deviceMgr = {
_: null,
set(usbDevice){
this._ = usbDevice || null;
return this;
},
close(){
const { _: usbDevice } = this;
return usbDevice ? usbDevice.close() : Promise.resolve();
},
status(){
const { _: usbDevice } = this;
let getStatus = usbDevice
? usbDevice.getCloudConnectionStatus()
: Promise.resolve('unknown');

return getStatus.then(status => status.toLowerCase());
}
};

const options = { id: device, api: this._api, auth: this._auth };
const queryDevice = openUsbDeviceById(options)
.then(usbDevice => deviceMgr.set(usbDevice).status());

return spin(queryDevice, 'Querying device...')
.then(status => {
if (until && until !== status){
throw new Error(`Unexpected status: ${status}`);
}
console.log(status);
})
.catch(error => {
if (until){
return deviceMgr.close()
.then(() => this.cloudStatus(args, started));
}
throw error;
})
.finally(() => deviceMgr.close());
}

_forEachUsbDevice(args, func, { dfuMode = false } = {}){
const msg = 'Getting device information...';
const operation = this._openUsbDevices(args, { dfuMode });
Expand Down

0 comments on commit 8603007

Please sign in to comment.