device move: Stop fetching unnecessary device & app fields

Reduces the amount of device data retrieved
by 66%.

Change-type: patch
This commit is contained in:
Thodoris Greasidis 2023-05-20 00:58:32 +03:00
parent 8b3235ab2b
commit a2392dc580
3 changed files with 59 additions and 53 deletions

View File

@ -30,13 +30,6 @@ import { ExpectedError } from '../../errors';
import { getBalenaSdk, stripIndent } from '../../utils/lazy';
import { applicationIdInfo } from '../../utils/messages';
type ExtendedDevice = PineTypedResult<
Device,
typeof import('../../utils/helpers').expandForAppNameAndCpuArch
> & {
application_name?: string;
};
interface FlagsDef {
fleet?: string;
help: void;
@ -82,6 +75,33 @@ export default class DeviceMoveCmd extends Command {
public static authenticated = true;
private async getDevices(balena: BalenaSDK, deviceUuids: string[]) {
const deviceOptions = {
$select: 'belongs_to__application',
$expand: {
is_of__device_type: {
$select: 'is_of__cpu_architecture',
$expand: {
is_of__cpu_architecture: {
$select: 'slug',
},
},
},
},
} satisfies PineOptions<Device>;
// TODO: Refacor once `device.get()` accepts an array of uuids`
const devices = await Promise.all(
deviceUuids.map(
(uuid) =>
balena.models.device.get(uuid, deviceOptions) as Promise<
PineTypedResult<Device, typeof deviceOptions>
>,
),
);
return devices;
}
public async run() {
const { args: params, flags: options } = this.parse<FlagsDef, ArgsDef>(
DeviceMoveCmd,
@ -89,36 +109,17 @@ export default class DeviceMoveCmd extends Command {
const balena = getBalenaSdk();
const { expandForAppNameAndCpuArch } = await import('../../utils/helpers');
// Split uuids string into array of uuids
const deviceUuids = params.uuid.split(',');
// Get devices
const devices = await Promise.all(
deviceUuids.map(
(uuid) =>
balena.models.device.get(
uuid,
expandForAppNameAndCpuArch,
) as Promise<ExtendedDevice>,
),
);
// Map application name for each device
for (const device of devices) {
const belongsToApplication = device.belongs_to__application;
device.application_name = belongsToApplication?.[0]
? belongsToApplication[0].app_name
: 'N/a';
}
const devices = await this.getDevices(balena, deviceUuids);
// Disambiguate application
const { getApplication } = await import('../../utils/sdk');
// Get destination application
const application = options.fleet
? await getApplication(balena, options.fleet)
? await getApplication(balena, options.fleet, { $select: ['id', 'slug'] })
: await this.interactivelySelectApplication(balena, devices);
// Move each device
@ -135,7 +136,7 @@ export default class DeviceMoveCmd extends Command {
async interactivelySelectApplication(
balena: BalenaSDK,
devices: ExtendedDevice[],
devices: Awaited<ReturnType<typeof this.getDevices>>,
) {
const { getExpandedProp } = await import('../../utils/pine');
// deduplicate the slugs
@ -181,7 +182,9 @@ export default class DeviceMoveCmd extends Command {
const application = await patterns.selectApplication(
(app) =>
compatibleDeviceTypeSlugs.has(app.is_for__device_type[0].slug) &&
devices.some((device) => device.application_name !== app.app_name),
devices.some(
(device) => device.belongs_to__application.__id !== app.id,
),
true,
);
return application;

View File

@ -437,20 +437,6 @@ export const expandForAppName = {
},
} satisfies BalenaSdk.PineOptions<BalenaSdk.Device>;
export const expandForAppNameAndCpuArch = {
$expand: {
...expandForAppName.$expand,
is_of__device_type: {
$select: 'slug',
$expand: {
is_of__cpu_architecture: {
$select: 'slug',
},
},
},
},
} satisfies BalenaSdk.PineOptions<BalenaSdk.Device>;
/**
* Use the `readline` library on Windows to install SIGINT handlers.
* This appears to be necessary on MSYS / Git for Windows, and also useful

View File

@ -14,7 +14,14 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import type { BalenaSDK, Device, Organization } from 'balena-sdk';
import type {
Application,
BalenaSDK,
Device,
Organization,
PineOptions,
PineTypedResult,
} from 'balena-sdk';
import _ = require('lodash');
import { instanceOf, NotLoggedInError, ExpectedError } from '../errors';
@ -157,18 +164,28 @@ export async function confirm(
}
}
export async function selectApplication(
filter?: (app: ApplicationWithDeviceType) => boolean,
errorOnEmptySelection = false,
) {
const balena = getBalenaSdk();
const apps = (await balena.models.application.getAllDirectlyAccessible({
const selectApplicationPineOptions = {
$select: ['id', 'slug', 'app_name'],
$expand: {
is_for__device_type: {
$select: 'slug',
},
},
})) as ApplicationWithDeviceType[];
} satisfies PineOptions<Application>;
type SelectApplicationResult = PineTypedResult<
Application,
typeof selectApplicationPineOptions
>;
export async function selectApplication(
filter?: (app: SelectApplicationResult) => boolean,
errorOnEmptySelection = false,
) {
const balena = getBalenaSdk();
const apps = (await balena.models.application.getAllDirectlyAccessible(
selectApplicationPineOptions,
)) as SelectApplicationResult[];
if (!apps.length) {
throw new ExpectedError('No fleets found');