From d78045b6ab0c78a1383fd240bf0f9e11b1af777f Mon Sep 17 00:00:00 2001 From: Brian Bugh <438465+bbugh@users.noreply.github.com> Date: Fri, 27 Oct 2023 11:54:36 -0500 Subject: [PATCH] device: Add `--json` option for JSON output change-type: minor --- docs/balena-cli.md | 11 ++++ lib/commands/device/index.ts | 76 +++++++++++++++--------- tests/commands/device/device.spec.ts | 15 +++++ tests/test-data/api-response/device.json | 6 ++ 4 files changed, 81 insertions(+), 27 deletions(-) diff --git a/docs/balena-cli.md b/docs/balena-cli.md index 93a98d07..adebd409 100644 --- a/docs/balena-cli.md +++ b/docs/balena-cli.md @@ -1261,10 +1261,17 @@ the uuid of the device to identify Show information about a single device. +The --json option is recommended when scripting the output of this command, +because field names are less likely to change in JSON format and because it +better represents data types like arrays, empty strings and null values. +The 'jq' utility may be helpful for querying JSON fields in shell scripts +(https://stedolan.github.io/jq/manual/). + Examples: $ balena device 7cf02a6 $ balena device 7cf02a6 --view + $ balena device 7cf02a6 --json ### Arguments @@ -1274,6 +1281,10 @@ the device uuid ### Options +#### -j, --json + +produce JSON output instead of tabular output + #### --view open device dashboard page diff --git a/lib/commands/device/index.ts b/lib/commands/device/index.ts index 99c23769..7ef83af0 100644 --- a/lib/commands/device/index.ts +++ b/lib/commands/device/index.ts @@ -20,6 +20,7 @@ import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { expandForAppName } from '../../utils/helpers'; import { getBalenaSdk, getVisuals, stripIndent } from '../../utils/lazy'; +import { jsonInfo } from '../../utils/messages'; import type { Application, Release } from 'balena-sdk'; @@ -45,10 +46,13 @@ export default class DeviceCmd extends Command { Show info about a single device. Show information about a single device. + + ${jsonInfo.split('\n').join('\n\t\t')} `; public static examples = [ '$ balena device 7cf02a6', '$ balena device 7cf02a6 --view', + '$ balena device 7cf02a6 --json', ]; public static args = { @@ -61,6 +65,7 @@ export default class DeviceCmd extends Command { public static usage = 'device '; public static flags = { + json: cf.json, help: cf.help, view: Flags.boolean({ default: false, @@ -76,33 +81,45 @@ export default class DeviceCmd extends Command { const balena = getBalenaSdk(); - const device = (await balena.models.device.get(params.uuid, { - $select: [ - 'device_name', - 'id', - 'overall_status', - 'is_online', - 'ip_address', - 'mac_address', - 'last_connectivity_event', - 'uuid', - 'supervisor_version', - 'is_web_accessible', - 'note', - 'os_version', - 'memory_usage', - 'memory_total', - 'public_address', - 'storage_block_device', - 'storage_usage', - 'storage_total', - 'cpu_usage', - 'cpu_temp', - 'cpu_id', - 'is_undervolted', - ], - ...expandForAppName, - })) as ExtendedDevice; + const device = (await balena.models.device.get( + params.uuid, + options.json + ? { + $expand: { + device_tag: { + $select: ['tag_key', 'value'], + }, + ...expandForAppName.$expand, + }, + } + : { + $select: [ + 'device_name', + 'id', + 'overall_status', + 'is_online', + 'ip_address', + 'mac_address', + 'last_connectivity_event', + 'uuid', + 'supervisor_version', + 'is_web_accessible', + 'note', + 'os_version', + 'memory_usage', + 'memory_total', + 'public_address', + 'storage_block_device', + 'storage_usage', + 'storage_total', + 'cpu_usage', + 'cpu_temp', + 'cpu_id', + 'is_undervolted', + ], + ...expandForAppName, + }, + )) as ExtendedDevice; if (options.view) { const open = await import('open'); @@ -166,6 +183,11 @@ export default class DeviceCmd extends Command { ); } + if (options.json) { + console.log(JSON.stringify(device, null, 4)); + return; + } + console.log( getVisuals().table.vertical(device, [ `$${device.device_name}$`, diff --git a/tests/commands/device/device.spec.ts b/tests/commands/device/device.spec.ts index 752a7437..7cd2c7a5 100644 --- a/tests/commands/device/device.spec.ts +++ b/tests/commands/device/device.spec.ts @@ -107,4 +107,19 @@ describe('balena device', function () { expect(lines[0]).to.equal('== SPARKLING WOOD'); expect(lines[6].split(':')[1].trim()).to.equal('N/a'); }); + + it('outputs device as JSON with the -j/--json flag', async () => { + api.scope + .get(/^\/v6\/device\?.+&\$expand=device_tag\(\$select=tag_key,value\)/) + .replyWithFile(200, path.join(apiResponsePath, 'device.json'), { + 'Content-Type': 'application/json', + }); + + const { out, err } = await runCommand('device 27fda508c --json'); + expect(err).to.be.empty; + const json = JSON.parse(out.join('')); + expect(json.device_name).to.equal('sparkling-wood'); + expect(json.belongs_to__application[0].app_name).to.equal('test app'); + expect(json.device_tag[0].tag_key).to.equal('example'); + }); }); diff --git a/tests/test-data/api-response/device.json b/tests/test-data/api-response/device.json index 4a3deee4..e6505ca5 100644 --- a/tests/test-data/api-response/device.json +++ b/tests/test-data/api-response/device.json @@ -8,6 +8,12 @@ "__metadata": {} } ], + "device_tag": [ + { + "tag_key": "example", + "value": "true" + } + ], "id": 1747415, "is_managed_by__device": null, "device_name": "sparkling-wood",