Skip to content

Commit

Permalink
add cloud-status usb command to query device for its cloud connection…
Browse files Browse the repository at this point in the history
… status
  • Loading branch information
busticated committed Apr 25, 2020
1 parent 632f391 commit 8977b22
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 0 deletions.
23 changes: 23 additions & 0 deletions src/cli/usb.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,29 @@ module.exports = ({ commandProcessor, root }) => {
}
});

commandProcessor.createCommand(usb, 'cloud-status', 'Check a device\'s cloud connection state', {
params: '<deviceID>',
options: {
'until': {
description: 'Poll your device for a specific connection state and then exit',
choices: [
'unknown',
'disconnected',
'connecting',
'connected',
'disconnecting'
]
}
},
examples: {
'$0 $command 0123456789abcdef01234567': 'Check the cloud connection status for the device with id `0123456789abcdef01234567`',
'$0 $command 0123456789abcdef01234567 --until connected': 'Poll cloud connection status for device `0123456789abcdef01234567` until it reports `connected`'
},
handler: (args) => {
return usbCommand().cloudStatus(args);
}
});

return usb;
};

55 changes: 55 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,59 @@ describe('USB Command-Line Interface', () => {
});
});
});

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

it('Errors when required `deviceID` 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 \'deviceID\' is required.');
expect(argv.clierror).to.have.property('data', 'deviceID');
expect(argv.clierror).to.have.property('isUsageError', true);
expect(argv.params).to.eql({});
expect(argv.until).to.equal(undefined);
});

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

it('Errors when invalid option value is provided', () => {
const argv = commandProcessor.parse(root, ['usb', 'cloud-status', '4321', '--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({ deviceID: '4321' });
expect(argv.until).to.equal('NOPE');
});

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] <deviceID>',
'',
'Options:',
' --until Poll your device for a specific connection state and then exit [string] [choices: "unknown", "disconnected", "connecting", "connected", "disconnecting"]',
'',
'Examples:',
' particle usb cloud-status 0123456789abcdef01234567 Check the cloud connection status for the device with id `0123456789abcdef01234567`',
' particle usb cloud-status 0123456789abcdef01234567 --until connected Poll cloud connection status for device `0123456789abcdef01234567` until it reports `connected`',
''
].join(os.EOL));
});
});
});
});

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, defaultTimeout = 60 * 1000, started = Date.now()){
const timeout = defaultTimeout; // TODO (mirande): add `--timeout` flag
const { until, params: { deviceID } } = args;

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: deviceID, api: this._api, auth: this._auth };
const queryDevice = openUsbDeviceById(options)
.then(usbDevice => deviceMgr.set(usbDevice))
.then(deviceMgr => deviceMgr.status());

return spin(queryDevice, 'Querying device...')
.then(status => {
if (until && until !== status){
throw new Error(`Unexpected status: ${status}`);
}
console.log(status);
})
.catch(error => {
if (Date.now() - (started + timeout) > 0){
throw new Error('timed-out waiting for status...');
} else if (until){
return deviceMgr.close()
.then(() => this.cloudStatus(args, timeout, 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 8977b22

Please sign in to comment.