mirror of
https://github.com/balena-io/balena-cli.git
synced 2024-12-19 05:37:51 +00:00
utils/promote: Do not rely on the user to always be there
Change-type: minor Depends-on: https://github.com/resin-io/resin-sdk/pull/595 Signed-off-by: Thodoris Greasidis <thodoris@balena.io>
This commit is contained in:
parent
31628cfdcb
commit
f60d857c93
@ -173,6 +173,47 @@ async function getOrSelectLocalDevice(deviceIp?: string): Promise<string> {
|
||||
return ip;
|
||||
}
|
||||
|
||||
async function getApplicationsWithOptionalUsers(sdk: ResinSdk.ResinSDK,
|
||||
options: ResinSdk.PineOptionsFor<ResinSdk.Application>) {
|
||||
const _ = await import('lodash');
|
||||
|
||||
let applications = await sdk.models.application.getAll(options);
|
||||
// If we got more than one application with the same name it means that the
|
||||
// user has access to a collab app with the same name as a personal app.
|
||||
if (applications.length !== _.uniqBy(applications, 'app_name').length) {
|
||||
options = _.merge(_.cloneDeep(options), {
|
||||
$expand: { user: { $select: ['username'] } }
|
||||
});
|
||||
applications = await sdk.models.application.getAll(options);
|
||||
}
|
||||
|
||||
return applications;
|
||||
}
|
||||
|
||||
|
||||
async function selectAppFromList(applications: ResinSdk.Application[]) {
|
||||
const _ = await import('lodash');
|
||||
const { selectFromList } = await import('../utils/patterns');
|
||||
|
||||
// If we got more than one application with the same name it means that the
|
||||
// user has access to a collab app with the same name as a personal app. We
|
||||
// present a list to the user which shows the fully qualified application
|
||||
// name (user/appname) and allows them to select.
|
||||
const hasSameNameApps = applications.length !== _.uniqBy(applications, 'app_name').length
|
||||
|
||||
return selectFromList(
|
||||
hasSameNameApps ? 'Found multiple applications with that name; please select the one to use' : 'Select application',
|
||||
_.map(applications, app => {
|
||||
let name = app.app_name;
|
||||
if (hasSameNameApps) {
|
||||
const owner = _.get(app, 'user[0].username');
|
||||
name = `${owner}/${app.app_name}`;
|
||||
}
|
||||
return _.merge({ name }, app);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async function getOrSelectApplication(
|
||||
sdk: ResinSdk.ResinSDK,
|
||||
deviceType: string,
|
||||
@ -180,7 +221,6 @@ async function getOrSelectApplication(
|
||||
): Promise<ResinSdk.Application> {
|
||||
const _ = await import('lodash');
|
||||
const form = await import('resin-cli-form');
|
||||
const { selectFromList } = await import('../utils/patterns');
|
||||
|
||||
const allDeviceTypes = await sdk.models.config.getDeviceTypes();
|
||||
const deviceTypeManifest = _.find(allDeviceTypes, { slug: deviceType });
|
||||
@ -192,14 +232,14 @@ async function getOrSelectApplication(
|
||||
.map(type => type.slug)
|
||||
.value();
|
||||
|
||||
const options: any = {
|
||||
$expand: { user: { $select: ['username'] } },
|
||||
$filter: { device_type: { $in: compatibleDeviceTypes } },
|
||||
};
|
||||
|
||||
if (!appName) {
|
||||
const options = {
|
||||
$filter: { device_type: { $in: compatibleDeviceTypes } },
|
||||
};
|
||||
|
||||
// No application specified, show a list to select one.
|
||||
const applications = await sdk.models.application.getAll(options);
|
||||
const applications = await getApplicationsWithOptionalUsers(sdk, options);
|
||||
|
||||
if (applications.length === 0) {
|
||||
const shouldCreateApp = await form.ask({
|
||||
message:
|
||||
@ -213,29 +253,31 @@ async function getOrSelectApplication(
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
return selectFromList(
|
||||
'Select application',
|
||||
_.map(applications, app => _.merge({ name: app.app_name }, app)),
|
||||
);
|
||||
|
||||
return selectAppFromList(applications);
|
||||
}
|
||||
|
||||
// We're given an application; resolve it if it's ambiguous and also validate
|
||||
// it's of appropriate device type.
|
||||
options.$filter = { app_name: appName };
|
||||
const options: ResinSdk.PineOptionsFor<ResinSdk.Application> = {};
|
||||
|
||||
// Check for an app of the form `user/application` and update the API query.
|
||||
const match = appName.split('/');
|
||||
if (match.length > 1) {
|
||||
// These will match at most one app, so we'll return early.
|
||||
options.$expand.user.$filter = { username: match[0] };
|
||||
options.$filter.app_name = match[1];
|
||||
// These will match at most one app
|
||||
options.$expand = {
|
||||
user: {
|
||||
$select: ['username'],
|
||||
$filter: { username: match[0] },
|
||||
},
|
||||
};
|
||||
|
||||
options.$filter = { app_name: match[1] };
|
||||
} else {
|
||||
// We're given an application; resolve it if it's ambiguous and also validate
|
||||
// it's of appropriate device type.
|
||||
options.$filter = { app_name: appName };
|
||||
}
|
||||
|
||||
// Fetch all applications with the given name that are accessible to the user
|
||||
const applications = await sdk.pine.get<ResinSdk.Application>({
|
||||
resource: 'application',
|
||||
options,
|
||||
});
|
||||
const applications = await getApplicationsWithOptionalUsers(sdk, options);
|
||||
|
||||
if (applications.length === 0) {
|
||||
const shouldCreateApp = await form.ask({
|
||||
@ -246,7 +288,7 @@ async function getOrSelectApplication(
|
||||
default: true,
|
||||
});
|
||||
if (shouldCreateApp) {
|
||||
return createApplication(sdk, deviceType, options.$filter.app_name);
|
||||
return createApplication(sdk, deviceType, options.$filter.app_name as string);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
@ -257,21 +299,15 @@ async function getOrSelectApplication(
|
||||
_.includes(compatibleDeviceTypes, app.device_type),
|
||||
);
|
||||
|
||||
if (validApplications.length === 0) {
|
||||
throw new Error('No application found with a matching device type')
|
||||
}
|
||||
|
||||
if (validApplications.length === 1) {
|
||||
return validApplications[0];
|
||||
}
|
||||
|
||||
// If we got more than one application with the same name it means that the
|
||||
// user has access to a collab app with the same name as a personal app. We
|
||||
// present a list to the user which shows the fully qualified application
|
||||
// name (user/appname) and allows them to select
|
||||
return selectFromList(
|
||||
'Found multiple applications with that name; please select the one to use',
|
||||
_.map(validApplications, app => {
|
||||
const owner = _.get(app, 'user[0].username');
|
||||
return _.merge({ name: `${owner}/${app.app_name}` }, app);
|
||||
}),
|
||||
);
|
||||
return selectAppFromList(applications);
|
||||
}
|
||||
|
||||
async function createApplication(
|
||||
@ -284,9 +320,6 @@ async function createApplication(
|
||||
const patterns = await import('./patterns');
|
||||
|
||||
const user = await sdk.auth.getUserId();
|
||||
const queryOptions = {
|
||||
$filter: { user },
|
||||
};
|
||||
|
||||
const appName = await new Promise<string>(async (resolve, reject) => {
|
||||
while (true) {
|
||||
@ -299,7 +332,9 @@ async function createApplication(
|
||||
});
|
||||
|
||||
try {
|
||||
await sdk.models.application.get(appName, queryOptions);
|
||||
await sdk.models.application.get(appName, {
|
||||
$filter: { user },
|
||||
});
|
||||
patterns.printErrorMessage(
|
||||
'You already have an application with that name; please choose another.',
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user