From 0327ed766d81bb84ce3208d44457898e6a30e76b Mon Sep 17 00:00:00 2001 From: Thodoris Greasidis Date: Tue, 7 Sep 2021 11:43:35 +0000 Subject: [PATCH] device move: Improve types & reduce the number of API requests Change-type: patch Signed-off-by: Thodoris Greasidis --- lib/commands/device/move.ts | 100 +++++++++++++++++++----------------- lib/utils/helpers.ts | 18 ++++++- 2 files changed, 68 insertions(+), 50 deletions(-) diff --git a/lib/commands/device/move.ts b/lib/commands/device/move.ts index bbad496c..16e1a025 100644 --- a/lib/commands/device/move.ts +++ b/lib/commands/device/move.ts @@ -17,7 +17,12 @@ import type { flags } from '@oclif/command'; import type { IArg } from '@oclif/parser/lib/args'; -import type { Application, BalenaSDK } from 'balena-sdk'; +import type { + BalenaSDK, + Device, + DeviceType, + PineTypedResult, +} from 'balena-sdk'; import Command from '../../command'; import * as cf from '../../utils/common-flags'; import { ExpectedError } from '../../errors'; @@ -29,9 +34,12 @@ import { } from '../../utils/messages'; import { isV13 } from '../../utils/version'; -interface ExtendedDevice extends DeviceWithDeviceType { +type ExtendedDevice = PineTypedResult< + Device, + typeof import('../../utils/helpers').expandForAppNameAndCpuArch +> & { application_name?: string; -} +}; interface FlagsDef { application?: string; @@ -94,7 +102,7 @@ export default class DeviceMoveCmd extends Command { const balena = getBalenaSdk(); const { tryAsInteger } = await import('../../utils/validation'); - const { expandForAppName } = await import('../../utils/helpers'); + const { expandForAppNameAndCpuArch } = await import('../../utils/helpers'); // Parse ids string into array of correct types const deviceIds: Array = params.uuid @@ -107,15 +115,14 @@ export default class DeviceMoveCmd extends Command { (uuid) => balena.models.device.get( uuid, - expandForAppName, + expandForAppNameAndCpuArch, ) as Promise, ), ); // Map application name for each device for (const device of devices) { - const belongsToApplication = - device.belongs_to__application as Application[]; + const belongsToApplication = device.belongs_to__application; device.application_name = belongsToApplication?.[0] ? belongsToApplication[0].app_name : 'N/a'; @@ -146,58 +153,55 @@ export default class DeviceMoveCmd extends Command { devices: ExtendedDevice[], ) { const { getExpandedProp } = await import('../../utils/pine'); - const [deviceDeviceTypes, deviceTypes] = await Promise.all([ - balena.models.deviceType.getAll({ - $select: 'slug', - $expand: { - is_of__cpu_architecture: { - $select: 'slug', - }, - }, - $filter: { - slug: { - // deduplicate the device type slugs - $in: Array.from( - new Set( - devices.map((device) => device.is_of__device_type[0].slug), - ), - ), - }, - }, - }), - balena.models.deviceType.getAllSupported({ - $select: 'slug', - $expand: { - is_of__cpu_architecture: { - $select: 'slug', - }, - }, - }), - ]); - - const compatibleDeviceTypes = deviceTypes.filter((dt) => { - const dtArch = getExpandedProp(dt.is_of__cpu_architecture, 'slug')!; - return deviceDeviceTypes.every((deviceDeviceType) => - balena.models.os.isArchitectureCompatibleWith( - getExpandedProp(deviceDeviceType.is_of__cpu_architecture, 'slug')!, - dtArch, + // deduplicate the slugs + const deviceCpuArchs = Array.from( + new Set( + devices.map( + (d) => d.is_of__device_type[0].is_of__cpu_architecture[0].slug, ), - ); - }); + ), + ); + + const deviceTypeOptions = { + $select: 'slug', + $expand: { + is_of__cpu_architecture: { + $select: 'slug', + }, + }, + } as const; + const deviceTypes = (await balena.models.deviceType.getAllSupported( + deviceTypeOptions, + )) as Array>; + + const compatibleDeviceTypeSlugs = new Set( + deviceTypes + .filter((deviceType) => { + const deviceTypeArch = getExpandedProp( + deviceType.is_of__cpu_architecture, + 'slug', + )!; + return deviceCpuArchs.every((deviceCpuArch) => + balena.models.os.isArchitectureCompatibleWith( + deviceCpuArch, + deviceTypeArch, + ), + ); + }) + .map((deviceType) => deviceType.slug), + ); 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, - ) && + compatibleDeviceTypeSlugs.has(app.is_for__device_type[0].slug) && devices.some((device) => device.application_name !== app.app_name), true, ); return application; } catch (err) { - if (deviceDeviceTypes.length) { + if (!compatibleDeviceTypeSlugs.size) { throw new ExpectedError( `${err.message}\nDo all devices have a compatible architecture?`, ); diff --git a/lib/utils/helpers.ts b/lib/utils/helpers.ts index f0b32743..135240b6 100644 --- a/lib/utils/helpers.ts +++ b/lib/utils/helpers.ts @@ -463,13 +463,27 @@ export function getProxyConfig(): ProxyConfig | undefined { } } -export const expandForAppName: BalenaSdk.PineOptions = { +export const expandForAppName = { $expand: { belongs_to__application: { $select: 'app_name' }, is_of__device_type: { $select: 'slug' }, is_running__release: { $select: 'commit' }, }, -}; +} as const; + +export const expandForAppNameAndCpuArch = { + $expand: { + ...expandForAppName.$expand, + is_of__device_type: { + $select: 'slug', + $expand: { + is_of__cpu_architecture: { + $select: 'slug', + }, + }, + }, + }, +} as const; /** * Use the `readline` library on Windows to install SIGINT handlers.