From ece4d88bfde3dd1749bbe618ed534bf2820a6711 Mon Sep 17 00:00:00 2001 From: Scott Lowe Date: Thu, 10 Sep 2020 11:58:16 +0200 Subject: [PATCH] Fix numerical id support in device move Change-type: patch Resolves: #2030 Signed-off-by: Scott Lowe --- lib/actions-oclif/device/move.ts | 127 +++++++++++++++++-------------- 1 file changed, 68 insertions(+), 59 deletions(-) diff --git a/lib/actions-oclif/device/move.ts b/lib/actions-oclif/device/move.ts index a67ceeb9..d4189edb 100644 --- a/lib/actions-oclif/device/move.ts +++ b/lib/actions-oclif/device/move.ts @@ -17,7 +17,7 @@ import { flags } from '@oclif/command'; import type { IArg } from '@oclif/parser/lib/args'; -import type { Application } from 'balena-sdk'; +import type { Application, BalenaSDK } from 'balena-sdk'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { expandForAppName } from '../../utils/helpers'; @@ -59,7 +59,6 @@ export default class DeviceMoveCmd extends Command { name: 'uuid', description: 'comma-separated list (no blank spaces) of device UUIDs to be moved', - parse: (dev) => tryAsInteger(dev), required: true, }, ]; @@ -81,21 +80,25 @@ export default class DeviceMoveCmd extends Command { const balena = getBalenaSdk(); - // Consolidate application options options.application = options.application || options.app; delete options.app; + // Parse ids string into array of correct types + const deviceIds: Array = params.uuid + .split(',') + .map((id) => tryAsInteger(id)); + + // Get devices const devices = await Promise.all( - params.uuid - .split(',') - .map( - (uuid) => - balena.models.device.get(uuid, expandForAppName) as Promise< - ExtendedDevice - >, - ), + deviceIds.map( + (uuid) => + balena.models.device.get(uuid, expandForAppName) as Promise< + ExtendedDevice + >, + ), ); + // Map application name for each device for (const device of devices) { const belongsToApplication = device.belongs_to__application as Application[]; device.application_name = belongsToApplication?.[0] @@ -104,55 +107,12 @@ export default class DeviceMoveCmd extends Command { } // Get destination application - let application; - if (options.application) { - application = options.application; - } else { - const [deviceDeviceTypes, deviceTypes] = await Promise.all([ - Promise.all( - devices.map((device) => - balena.models.device.getManifestBySlug( - device.is_of__device_type[0].slug, - ), - ), - ), - balena.models.config.getDeviceTypes(), - ]); + const application = + options.application || + (await this.interactivelySelectApplication(balena, devices)); - const compatibleDeviceTypes = deviceTypes.filter((dt) => - deviceDeviceTypes.every( - (deviceDeviceType) => - balena.models.os.isArchitectureCompatibleWith( - deviceDeviceType.arch, - dt.arch, - ) && - !!dt.isDependent === !!deviceDeviceType.isDependent && - dt.state !== 'DISCONTINUED', - ), - ); - - const patterns = await import('../../utils/patterns'); - try { - application = await patterns.selectApplication( - (app) => - compatibleDeviceTypes.some( - (dt) => dt.slug === app.is_for__device_type[0].slug, - ) && - // @ts-ignore using the extended device object prop - devices.some((device) => device.application_name !== app.app_name), - true, - ); - } catch (err) { - if (deviceDeviceTypes.length) { - throw new ExpectedError( - `${err.message}\nDo all devices have a compatible architecture?`, - ); - } - throw err; - } - } - - for (const uuid of params.uuid.split(',')) { + // Move each device + for (const uuid of deviceIds) { try { await balena.models.device.move(uuid, tryAsInteger(application)); console.info(`${uuid} was moved to ${application}`); @@ -162,4 +122,53 @@ export default class DeviceMoveCmd extends Command { } } } + + async interactivelySelectApplication( + balena: BalenaSDK, + devices: ExtendedDevice[], + ) { + const [deviceDeviceTypes, deviceTypes] = await Promise.all([ + Promise.all( + devices.map((device) => + balena.models.device.getManifestBySlug( + device.is_of__device_type[0].slug, + ), + ), + ), + balena.models.config.getDeviceTypes(), + ]); + + const compatibleDeviceTypes = deviceTypes.filter((dt) => + deviceDeviceTypes.every( + (deviceDeviceType) => + balena.models.os.isArchitectureCompatibleWith( + deviceDeviceType.arch, + dt.arch, + ) && + !!dt.isDependent === !!deviceDeviceType.isDependent && + dt.state !== 'DISCONTINUED', + ), + ); + + const patterns = await import('../../utils/patterns'); + try { + const application = await patterns.selectApplication( + (app) => + compatibleDeviceTypes.some( + (dt) => dt.slug === app.is_for__device_type[0].slug, + ) && + // @ts-ignore using the extended device object prop + devices.some((device) => device.application_name !== app.app_name), + true, + ); + return application; + } catch (err) { + if (deviceDeviceTypes.length) { + throw new ExpectedError( + `${err.message}\nDo all devices have a compatible architecture?`, + ); + } + throw err; + } + } }