diff --git a/ui/core/app/decorators/confirm.js b/ui/core/app/decorators/confirm.js index 755ff2f59..49db19c97 100644 --- a/ui/core/app/decorators/confirm.js +++ b/ui/core/app/decorators/confirm.js @@ -17,7 +17,7 @@ export function confirm(confirmationTextKey) { // If user confirmed, execute the decorated method .then(() => method.apply(this, arguments)) // If the user denied, gracefully handle promise rejection with no-op - .catch(() => {}); + .catch((e) => e); }; } } diff --git a/ui/core/app/decorators/notify.js b/ui/core/app/decorators/notify.js new file mode 100644 index 000000000..7232dfffd --- /dev/null +++ b/ui/core/app/decorators/notify.js @@ -0,0 +1,81 @@ +import { getOwner } from '@ember/application'; + +/** + * Decorates a method and, if the method does not error, shows a success + * notification via the notify service. The text of the notification is derived + * from the argument `notification`, which may be either a string or a function + * that returns a string. This string may optionally be an intl key. + * @param {string|function} notification + */ +export function notifySuccess(notification) { + return function (_target, _propertyKey, desc) { + const method = desc.value; + desc.value = async function () { + const owner = getOwner(this); + const notifyService = owner.lookup('service:notify'); + const intlService = owner.lookup('service:intl'); + const candidateKey = (typeof notification === 'function') + ? notification.apply(this, arguments) + : notification; + // As of now, we only use translated strings for notifications, + // but this code would support both translated and arbitrary strings. + // const text = intlService.exists(candidateKey) + // ? intlService.t(candidateKey) + // : candidateKey; + const text = intlService.t(candidateKey); + const value = await method.apply(this, arguments); + notifyService.success(text); + return value; + }; + } +} + +/** + * Decorates a method and, if the method errors, shows an error + * notification via the notify service. The text of the notification is derived + * from the argument `notification`, which may be either a string or a function + * that returns a string. This string may optionally be an intl key. + * + * If `options.catch` is `true`, any error occurring in the decorated method + * will be rethrown after notification. The default is to rethrow errors. + * + * @param {string|function} notification + * @param {object} options + * @param {object} options.catch - defaults to false + */ +export function notifyError(notification, options={ catch: false }) { + return function (_target, _propertyKey, desc) { + const method = desc.value; + desc.value = async function () { + const owner = getOwner(this); + const notifyService = owner.lookup('service:notify'); + //const intlService = owner.lookup('service:intl'); + try { + return await method.apply(this, arguments); + } catch (error) { + + // As of now all error notifications pass a function, but this code + // could support either a function or a string. + // const candidateKey = (typeof notification === 'function') + // ? notification.apply(this, [error]) + // : notification; + const candidateKey = notification.apply(this, [error]); + + // As of now, we only use untranslated strings for error notifications, + // but this code would support both translated and arbitrary strings. + // const text = intlService.exists(candidateKey) + // ? intlService.t(candidateKey) + // : candidateKey; + const text = candidateKey; + + notifyService.error(text, { closeAfter: null }); + if (options.catch) { + // squelch the error + } else { + // rethrow the error + throw error; + } + } + }; + } +} diff --git a/ui/core/app/routes/account/change-password.js b/ui/core/app/routes/account/change-password.js index f17ec6e84..e4247da8e 100644 --- a/ui/core/app/routes/account/change-password.js +++ b/ui/core/app/routes/account/change-password.js @@ -1,6 +1,7 @@ import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; import { action } from '@ember/object'; +import { notifySuccess, notifyError } from '../../decorators/notify'; /** * The change password flow is associated only with password-type auth methods. @@ -33,20 +34,16 @@ export default class AccountChangePasswordRoute extends Route { * @param {string} newPassword */ @action + @notifyError(({ message }) => message, { catch: true }) + @notifySuccess('notifications.save-success') async changePassword(account, currentPassword, newPassword) { - try { - await account.changePassword(currentPassword, newPassword); - // Transition to index after success. We ignore errors in the transition, - // since the index route is responsible for additional - // redirects depending on the authentication context. These additional - // redirects may abort this initial transition, resulting in a - // TransitionAborted error, which we do not want to show to the user. - await this.replaceWith('index').catch(() => {}); - this.notify.success(this.intl.t('notifications.save-success')); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } + await account.changePassword(currentPassword, newPassword); + // Transition to index after success. We ignore errors in the transition, + // since the index route is responsible for additional + // redirects depending on the authentication context. These additional + // redirects may abort this initial transition, resulting in a + // TransitionAborted error, which we do not want to show to the user. + await this.replaceWith('index').catch(() => {}); } /** diff --git a/ui/core/app/routes/scopes/scope/auth-methods.js b/ui/core/app/routes/scopes/scope/auth-methods.js index c91387c71..020ed8b9f 100644 --- a/ui/core/app/routes/scopes/scope/auth-methods.js +++ b/ui/core/app/routes/scopes/scope/auth-methods.js @@ -3,6 +3,7 @@ import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import loading from 'ember-loading/decorator'; import { confirm } from '../../../decorators/confirm'; +import { notifySuccess, notifyError } from '../../../decorators/notify'; export default class ScopesScopeAuthMethodsRoute extends Route { // =services @@ -47,23 +48,15 @@ export default class ScopesScopeAuthMethodsRoute extends Route { */ @action @loading + @notifyError(({ message }) => message) + @notifySuccess(({ isNew }) => isNew ? 'notifications.create-success' : 'notifications.save-success') async save(authMethod) { - const { isNew } = authMethod; - try { - await authMethod.save(); - await this.transitionTo( - 'scopes.scope.auth-methods.auth-method', - authMethod - ); - this.refresh(); - this.notify.success( - this.intl.t(isNew ? 'notifications.create-success' : 'notifications.save-success') - ); - } catch (error) { - //TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - throw error; - } + await authMethod.save(); + await this.transitionTo( + 'scopes.scope.auth-methods.auth-method', + authMethod + ); + this.refresh(); } /** @@ -73,15 +66,11 @@ export default class ScopesScopeAuthMethodsRoute extends Route { @action @loading @confirm('questions.delete-confirm') + @notifyError(({ message }) => message, { catch: true }) + @notifySuccess('notifications.delete-success') async delete(authMethod) { - try { - await authMethod.destroyRecord(); - await this.replaceWith('scopes.scope.auth-methods'); - this.refresh(); - this.notify.success(this.intl.t('notifications.delete-success')); - } catch (error) { - //TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } + await authMethod.destroyRecord(); + await this.replaceWith('scopes.scope.auth-methods'); + this.refresh(); } } diff --git a/ui/core/app/routes/scopes/scope/auth-methods/auth-method/accounts.js b/ui/core/app/routes/scopes/scope/auth-methods/auth-method/accounts.js index 93cbefd58..94871fd6f 100644 --- a/ui/core/app/routes/scopes/scope/auth-methods/auth-method/accounts.js +++ b/ui/core/app/routes/scopes/scope/auth-methods/auth-method/accounts.js @@ -3,6 +3,7 @@ import { action } from '@ember/object'; import { inject as service } from '@ember/service'; import loading from 'ember-loading/decorator'; import { confirm } from '../../../../../decorators/confirm'; +import { notifySuccess, notifyError } from '../../../../../decorators/notify'; export default class ScopesScopeAuthMethodsAuthMethodAccountsRoute extends Route { // =services @@ -43,27 +44,20 @@ export default class ScopesScopeAuthMethodsAuthMethodAccountsRoute extends Route */ @action @loading + @notifyError(({ message }) => message) + @notifySuccess(({ isNew }) => isNew ? 'notifications.create-success' : 'notifications.save-success') async save(account, password) { const { isNew } = account; const adapterOptions = {}; if (isNew) { adapterOptions.password = password; } - try { - await account.save({ adapterOptions }); - await this.transitionTo( - 'scopes.scope.auth-methods.auth-method.accounts.account', - account - ); - this.refresh(); - this.notify.success( - this.intl.t(isNew ? 'notifications.create-success' : 'notifications.save-success') - ); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - throw error; - } + await account.save({ adapterOptions }); + await this.transitionTo( + 'scopes.scope.auth-methods.auth-method.accounts.account', + account + ); + this.refresh(); } /** @@ -73,15 +67,11 @@ export default class ScopesScopeAuthMethodsAuthMethodAccountsRoute extends Route @action @loading @confirm('questions.delete-confirm') + @notifyError(({ message }) => message, { catch: true }) + @notifySuccess('notifications.delete-success') async deleteAccount(account) { - try { - await account.destroyRecord(); - await this.replaceWith('scopes.scope.auth-methods.auth-method.accounts'); - this.refresh(); - this.notify.success(this.intl.t('notifications.delete-success')); - } catch (error) { - //TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } + await account.destroyRecord(); + await this.replaceWith('scopes.scope.auth-methods.auth-method.accounts'); + this.refresh(); } } diff --git a/ui/core/app/routes/scopes/scope/auth-methods/auth-method/accounts/account/set-password.js b/ui/core/app/routes/scopes/scope/auth-methods/auth-method/accounts/account/set-password.js index 900c5eaba..2f5886030 100644 --- a/ui/core/app/routes/scopes/scope/auth-methods/auth-method/accounts/account/set-password.js +++ b/ui/core/app/routes/scopes/scope/auth-methods/auth-method/accounts/account/set-password.js @@ -2,6 +2,7 @@ import Route from '@ember/routing/route'; import { action } from '@ember/object'; import { inject as service } from '@ember/service'; import loading from 'ember-loading/decorator'; +import { notifySuccess, notifyError } from '../../../../../../../decorators/notify'; export default class ScopesScopeAuthMethodsAuthMethodAccountsAccountSettingsRoute extends Route { // =services @@ -35,16 +36,12 @@ export default class ScopesScopeAuthMethodsAuthMethodAccountsAccountSettingsRout */ @action @loading + @notifyError(({ message }) => message, { catch: true }) + @notifySuccess('notifications.save-success') async setPassword(account, password) { - try { - await account.setPassword(password); - await this.replaceWith( - 'scopes.scope.auth-methods.auth-method.accounts.account.set-password' - ); - this.notify.success(this.intl.t('notifications.save-success')); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } + await account.setPassword(password); + await this.replaceWith( + 'scopes.scope.auth-methods.auth-method.accounts.account.set-password' + ); } } diff --git a/ui/core/app/routes/scopes/scope/groups.js b/ui/core/app/routes/scopes/scope/groups.js index 1d04d7bb5..38085bfc7 100644 --- a/ui/core/app/routes/scopes/scope/groups.js +++ b/ui/core/app/routes/scopes/scope/groups.js @@ -3,6 +3,7 @@ import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import loading from 'ember-loading/decorator'; import { confirm } from '../../../decorators/confirm'; +import { notifySuccess, notifyError } from '../../../decorators/notify'; export default class ScopesScopeGroupsRoute extends Route { // =services @@ -47,20 +48,12 @@ export default class ScopesScopeGroupsRoute extends Route { */ @action @loading + @notifyError(({ message }) => message) + @notifySuccess(({ isNew }) => isNew ? 'notifications.create-success' : 'notifications.save-success') async save(group) { - const { isNew } = group; - try { - await group.save(); - await this.transitionTo('scopes.scope.groups.group', group); - this.refresh(); - this.notify.success( - this.intl.t(isNew ? 'notifications.create-success' : 'notifications.save-success') - ); - } catch (error) { - //TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - throw error; - } + await group.save(); + await this.transitionTo('scopes.scope.groups.group', group); + this.refresh(); } /** @@ -70,15 +63,11 @@ export default class ScopesScopeGroupsRoute extends Route { @action @loading @confirm('questions.delete-confirm') + @notifyError(({ message }) => message, { catch: true }) + @notifySuccess('notifications.delete-success') async delete(group) { - try { - await group.destroyRecord(); - await this.replaceWith('scopes.scope.groups'); - this.refresh(); - this.notify.success(this.intl.t('notifications.delete-success')); - } catch (error) { - //TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } + await group.destroyRecord(); + await this.replaceWith('scopes.scope.groups'); + this.refresh(); } } diff --git a/ui/core/app/routes/scopes/scope/groups/group/add-members.js b/ui/core/app/routes/scopes/scope/groups/group/add-members.js index 240e0a442..d4f392684 100644 --- a/ui/core/app/routes/scopes/scope/groups/group/add-members.js +++ b/ui/core/app/routes/scopes/scope/groups/group/add-members.js @@ -3,6 +3,7 @@ import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import { hash } from 'rsvp'; import loading from 'ember-loading/decorator'; +import { notifySuccess, notifyError } from '../../../../../decorators/notify'; export default class ScopesScopeGroupsGroupAddMembersRoute extends Route { // =services @@ -65,15 +66,11 @@ export default class ScopesScopeGroupsGroupAddMembersRoute extends Route { */ @action @loading + @notifyError(({ message }) => message, { catch: true }) + @notifySuccess('notifications.add-success') async addMembers(group, userIDs) { - try { - await group.addMembers(userIDs); - await this.replaceWith('scopes.scope.groups.group.members'); - this.notify.success(this.intl.t('notifications.add-success')); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } + await group.addMembers(userIDs); + await this.replaceWith('scopes.scope.groups.group.members'); } /** diff --git a/ui/core/app/routes/scopes/scope/groups/group/members.js b/ui/core/app/routes/scopes/scope/groups/group/members.js index 4cf76322c..be4f97343 100644 --- a/ui/core/app/routes/scopes/scope/groups/group/members.js +++ b/ui/core/app/routes/scopes/scope/groups/group/members.js @@ -4,6 +4,7 @@ import { inject as service } from '@ember/service'; import { hash, all } from 'rsvp'; import loading from 'ember-loading/decorator'; import { confirm } from '../../../../../decorators/confirm'; +import { notifySuccess, notifyError } from '../../../../../decorators/notify'; export default class ScopesScopeGroupsGroupMembersRoute extends Route { // =services @@ -40,14 +41,10 @@ export default class ScopesScopeGroupsGroupMembersRoute extends Route { @action @loading @confirm('questions.remove-confirm') + @notifyError(({ message }) => message, { catch: true }) + @notifySuccess('notifications.remove-success') async removeMember(group, member) { - try { - await group.removeMember(member.id); - this.refresh(); - this.notify.success(this.intl.t('notifications.remove-success')); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } + await group.removeMember(member.id); + this.refresh(); } } diff --git a/ui/core/app/routes/scopes/scope/orgs.js b/ui/core/app/routes/scopes/scope/orgs.js index 626de80fa..5aad80343 100644 --- a/ui/core/app/routes/scopes/scope/orgs.js +++ b/ui/core/app/routes/scopes/scope/orgs.js @@ -2,6 +2,7 @@ import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import loading from 'ember-loading/decorator'; +import { notifySuccess, notifyError } from '../../../decorators/notify'; export default class ScopesScopeOrgsRoute extends Route { // =services @@ -49,18 +50,11 @@ export default class ScopesScopeOrgsRoute extends Route { */ @action @loading + @notifyError(({ message }) => message, { catch: true }) + @notifySuccess('notifications.create-success') async save(org) { - try { - await org.save(); - await this.transitionTo('scopes.scope.projects', org); - this.refresh(); - this.notify.success( - this.intl.t('notifications.create-success') - ); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - throw error; - } + await org.save(); + await this.transitionTo('scopes.scope.projects', org); + this.refresh(); } } diff --git a/ui/core/app/routes/scopes/scope/projects.js b/ui/core/app/routes/scopes/scope/projects.js index c28b137a0..7a43e0e71 100644 --- a/ui/core/app/routes/scopes/scope/projects.js +++ b/ui/core/app/routes/scopes/scope/projects.js @@ -3,6 +3,7 @@ import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import loading from 'ember-loading/decorator'; import { confirm } from '../../../decorators/confirm'; +import { notifySuccess, notifyError } from '../../../decorators/notify'; export default class ScopesScopeProjectsRoute extends Route { // =services @@ -64,20 +65,12 @@ export default class ScopesScopeProjectsRoute extends Route { */ @action @loading + @notifyError(({ message }) => message) + @notifySuccess(({ isNew }) => isNew ? 'notifications.create-success' : 'notifications.save-success') async save(project) { - const { isNew } = project; - try { - await project.save(); - await this.transitionTo('scopes.scope.projects.project', project); - this.refresh(); - this.notify.success( - this.intl.t(isNew ? 'notifications.create-success' : 'notifications.save-success') - ); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - throw error; - } + await project.save(); + await this.transitionTo('scopes.scope.projects.project', project); + this.refresh(); } /** @@ -87,15 +80,11 @@ export default class ScopesScopeProjectsRoute extends Route { @action @loading @confirm('questions.delete-confirm') + @notifyError(({ message }) => message, { catch: true }) + @notifySuccess('notifications.delete-success') async delete(project) { - try { - await project.destroyRecord(); - await this.replaceWith('scopes.scope.projects'); - this.refresh(); - this.notify.success(this.intl.t('notifications.delete-success')); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } + await project.destroyRecord(); + await this.replaceWith('scopes.scope.projects'); + this.refresh(); } } diff --git a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs.js b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs.js index c8fce1129..ab01b5b7e 100644 --- a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs.js +++ b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs.js @@ -3,6 +3,7 @@ import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import loading from 'ember-loading/decorator'; import { confirm } from '../../../../../decorators/confirm'; +import { notifySuccess, notifyError } from '../../../../../decorators/notify'; export default class ScopesScopeProjectsProjectHostCatalogsRoute extends Route { // =services @@ -41,23 +42,15 @@ export default class ScopesScopeProjectsProjectHostCatalogsRoute extends Route { */ @action @loading + @notifyError(({ message }) => message) + @notifySuccess(({ isNew }) => isNew ? 'notifications.create-success' : 'notifications.save-success') async save(hostCatalog) { - const { isNew } = hostCatalog; - try { - await hostCatalog.save(); - await this.transitionTo( - 'scopes.scope.projects.project.host-catalogs.host-catalog', - hostCatalog - ); - this.refresh(); - this.notify.success( - this.intl.t(isNew ? 'notifications.create-success' : 'notifications.save-success') - ); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - throw error; - } + await hostCatalog.save(); + await this.transitionTo( + 'scopes.scope.projects.project.host-catalogs.host-catalog', + hostCatalog + ); + this.refresh(); } /** @@ -67,15 +60,11 @@ export default class ScopesScopeProjectsProjectHostCatalogsRoute extends Route { @action @loading @confirm('questions.delete-confirm') + @notifyError(({ message }) => message, { catch: true }) + @notifySuccess('notifications.delete-success') async delete(hostCatalog) { - try { - await hostCatalog.destroyRecord(); - await this.replaceWith('scopes.scope.projects.project.host-catalogs'); - this.refresh(); - this.notify.success(this.intl.t('notifications.delete-success')); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } + await hostCatalog.destroyRecord(); + await this.replaceWith('scopes.scope.projects.project.host-catalogs'); + this.refresh(); } } diff --git a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets.js b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets.js index 3c003bd50..380250c3e 100644 --- a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets.js +++ b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets.js @@ -3,6 +3,7 @@ import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import loading from 'ember-loading/decorator'; import { confirm } from '../../../../../../../decorators/confirm'; +import { notifySuccess, notifyError } from '../../../../../../../decorators/notify'; export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostSetsRoute extends Route { // =services @@ -46,23 +47,15 @@ export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostSetsRo */ @action @loading + @notifyError(({ message }) => message) + @notifySuccess(({ isNew }) => isNew ? 'notifications.create-success' : 'notifications.save-success') async save(hostSet) { - try { - const { isNew } = hostSet; - await hostSet.save(); - await this.transitionTo( - 'scopes.scope.projects.project.host-catalogs.host-catalog.host-sets.host-set', - hostSet - ); - this.refresh(); - this.notify.success( - this.intl.t(isNew ? 'notifications.create-success' : 'notifications.save-success') - ); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - throw error; - } + await hostSet.save(); + await this.transitionTo( + 'scopes.scope.projects.project.host-catalogs.host-catalog.host-sets.host-set', + hostSet + ); + this.refresh(); } /** @@ -72,16 +65,12 @@ export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostSetsRo @action @loading @confirm('questions.delete-confirm') + @notifyError(({ message }) => message, { catch: true }) + @notifySuccess('notifications.delete-success') async deleteHostSet(hostSet) { - try { - await hostSet.destroyRecord(); - await this.replaceWith( - 'scopes.scope.projects.project.host-catalogs.host-catalog.host-sets' - ); - this.notify.success(this.intl.t('notifications.delete-success')); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } + await hostSet.destroyRecord(); + await this.replaceWith( + 'scopes.scope.projects.project.host-catalogs.host-catalog.host-sets' + ); } } diff --git a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/host-set/add-hosts.js b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/host-set/add-hosts.js index d892490ab..3b398fcec 100644 --- a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/host-set/add-hosts.js +++ b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/host-set/add-hosts.js @@ -3,6 +3,7 @@ import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import { hash } from 'rsvp'; import loading from 'ember-loading/decorator'; +import { notifySuccess, notifyError } from '../../../../../../../../../decorators/notify'; export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostSetsHostSetAddHostsRoute extends Route { // =services @@ -70,17 +71,13 @@ export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostSetsHo */ @action @loading + @notifyError(({ message }) => message, { catch: true }) + @notifySuccess('notifications.add-success') async addHosts(hostSet, hostIDs) { - try { - await hostSet.addHosts(hostIDs); - await this.replaceWith( - 'scopes.scope.projects.project.host-catalogs.host-catalog.host-sets.host-set.hosts' - ); - this.notify.success(this.intl.t('notifications.add-success')); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } + await hostSet.addHosts(hostIDs); + await this.replaceWith( + 'scopes.scope.projects.project.host-catalogs.host-catalog.host-sets.host-set.hosts' + ); } /** diff --git a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/host-set/hosts.js b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/host-set/hosts.js index a4476caf1..8b4a5514d 100644 --- a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/host-set/hosts.js +++ b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/host-set/hosts.js @@ -4,6 +4,7 @@ import { action } from '@ember/object'; import { inject as service } from '@ember/service'; import loading from 'ember-loading/decorator'; import { confirm } from '../../../../../../../../../decorators/confirm'; +import { notifySuccess, notifyError } from '../../../../../../../../../decorators/notify'; export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostSetsHostSetHostsRoute extends Route { // =services @@ -39,20 +40,16 @@ export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostSetsHo @action @loading @confirm('questions.remove-confirm') + @notifyError(({ message }) => message, { catch: true }) + @notifySuccess('notifications.remove-success') async removeHost(hostSet, host) { - try { - const scopeID = this.modelFor('scopes.scope.projects.project').id; - const hostCatalogID = this.modelFor( - 'scopes.scope.projects.project.host-catalogs.host-catalog' - ).id; - await hostSet.removeHost(host.id, { - adapterOptions: { scopeID, hostCatalogID }, - }); - this.refresh(); - this.notify.success(this.intl.t('notifications.remove-success')); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } + const scopeID = this.modelFor('scopes.scope.projects.project').id; + const hostCatalogID = this.modelFor( + 'scopes.scope.projects.project.host-catalogs.host-catalog' + ).id; + await hostSet.removeHost(host.id, { + adapterOptions: { scopeID, hostCatalogID }, + }); + this.refresh(); } } diff --git a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts.js b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts.js index 86b7e1c75..9b9732e2a 100644 --- a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts.js +++ b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts.js @@ -3,6 +3,7 @@ import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import loading from 'ember-loading/decorator'; import { confirm } from '../../../../../../../decorators/confirm'; +import { notifySuccess, notifyError } from '../../../../../../../decorators/notify'; export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostsRoute extends Route { // =services @@ -46,23 +47,15 @@ export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostsRoute */ @action @loading + @notifyError(({ message }) => message) + @notifySuccess(({ isNew }) => isNew ? 'notifications.create-success' : 'notifications.save-success') async save(host) { - try { - const { isNew } = host; - await host.save(); - await this.transitionTo( - 'scopes.scope.projects.project.host-catalogs.host-catalog.hosts.host', - host - ); - this.refresh(); - this.notify.success( - this.intl.t(isNew ? 'notifications.create-success' : 'notifications.save-success') - ); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - throw error; - } + await host.save(); + await this.transitionTo( + 'scopes.scope.projects.project.host-catalogs.host-catalog.hosts.host', + host + ); + this.refresh(); } /** @@ -72,17 +65,13 @@ export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostsRoute @action @loading @confirm('questions.delete-confirm') + @notifyError(({ message }) => message, { catch: true }) + @notifySuccess('notifications.delete-success') async deleteHost(host) { - try { - await host.destroyRecord(); - await this.replaceWith( - 'scopes.scope.projects.project.host-catalogs.host-catalog.hosts' - ); - this.refresh(); - this.notify.success(this.intl.t('notifications.delete-success')); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } + await host.destroyRecord(); + await this.replaceWith( + 'scopes.scope.projects.project.host-catalogs.host-catalog.hosts' + ); + this.refresh(); } } diff --git a/ui/core/app/routes/scopes/scope/projects/project/sessions.js b/ui/core/app/routes/scopes/scope/projects/project/sessions.js index b537560a7..3e01b9f44 100644 --- a/ui/core/app/routes/scopes/scope/projects/project/sessions.js +++ b/ui/core/app/routes/scopes/scope/projects/project/sessions.js @@ -4,6 +4,7 @@ import { action } from '@ember/object'; import { all, hash } from 'rsvp'; import { task, timeout } from 'ember-concurrency'; import { A } from '@ember/array'; +import { notifySuccess, notifyError } from '../../../../../decorators/notify'; import config from '../../../../../config/environment'; const POLL_TIMEOUT_SECONDS = config.sessionPollingTimeoutSeconds; @@ -93,14 +94,10 @@ export default class ScopesScopeProjectsProjectSessionsRoute extends Route { * @param {SessionModel} */ @action + @notifyError(({ message }) => message, { catch: true }) + @notifySuccess('notifications.canceled-success') async cancelSession(session) { - try { - await session.cancelSession(); - this.notify.success(this.intl.t('notifications.canceled-success')); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } + await session.cancelSession(); } } diff --git a/ui/core/app/routes/scopes/scope/projects/project/targets.js b/ui/core/app/routes/scopes/scope/projects/project/targets.js index b2670942c..68e0b98d0 100644 --- a/ui/core/app/routes/scopes/scope/projects/project/targets.js +++ b/ui/core/app/routes/scopes/scope/projects/project/targets.js @@ -3,6 +3,7 @@ import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import loading from 'ember-loading/decorator'; import { confirm } from '../../../../../decorators/confirm'; +import { notifySuccess, notifyError } from '../../../../../decorators/notify'; export default class ScopesScopeProjectsProjectTargetsRoute extends Route { // =services @@ -41,20 +42,12 @@ export default class ScopesScopeProjectsProjectTargetsRoute extends Route { */ @action @loading + @notifyError(({ message }) => message) + @notifySuccess(({ isNew }) => isNew ? 'notifications.create-success' : 'notifications.save-success') async save(target) { - const { isNew } = target; - try { - await target.save(); - await this.transitionTo('scopes.scope.projects.project.targets.target', target); - this.refresh(); - this.notify.success( - this.intl.t(isNew ? 'notifications.create-success' : 'notifications.save-success') - ); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - throw error; - } + await target.save(); + await this.transitionTo('scopes.scope.projects.project.targets.target', target); + this.refresh(); } /** @@ -64,15 +57,11 @@ export default class ScopesScopeProjectsProjectTargetsRoute extends Route { @action @loading @confirm('questions.delete-confirm') + @notifyError(({ message }) => message, { catch: true }) + @notifySuccess('notifications.delete-success') async delete(target) { - try { - await target.destroyRecord(); - await this.replaceWith('scopes.scope.projects.project.targets'); - this.refresh(); - this.notify.success(this.intl.t('notifications.delete-success')); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } + await target.destroyRecord(); + await this.replaceWith('scopes.scope.projects.project.targets'); + this.refresh(); } } diff --git a/ui/core/app/routes/scopes/scope/projects/project/targets/target/add-host-sets.js b/ui/core/app/routes/scopes/scope/projects/project/targets/target/add-host-sets.js index 5736ae317..b747fda00 100644 --- a/ui/core/app/routes/scopes/scope/projects/project/targets/target/add-host-sets.js +++ b/ui/core/app/routes/scopes/scope/projects/project/targets/target/add-host-sets.js @@ -3,6 +3,7 @@ import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import { all } from 'rsvp'; import loading from 'ember-loading/decorator'; +import { notifySuccess, notifyError } from '../../../../../../../decorators/notify'; export default class ScopesScopeProjectsProjectTargetsTargetAddHostSetsRoute extends Route { // =services @@ -72,17 +73,13 @@ export default class ScopesScopeProjectsProjectTargetsTargetAddHostSetsRoute ext @action @loading + @notifyError(({ message }) => message, { catch: true }) + @notifySuccess('notifications.add-success') async save(target, hostSetIDs) { - try { - await target.addHostSets(hostSetIDs); - this.replaceWith( - 'scopes.scope.projects.project.targets.target.host-sets' - ); - this.notify.success(this.intl.t('notifications.add-success')); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } + await target.addHostSets(hostSetIDs); + this.replaceWith( + 'scopes.scope.projects.project.targets.target.host-sets' + ); } /** diff --git a/ui/core/app/routes/scopes/scope/projects/project/targets/target/host-sets.js b/ui/core/app/routes/scopes/scope/projects/project/targets/target/host-sets.js index dd9e52f0e..08d839f1f 100644 --- a/ui/core/app/routes/scopes/scope/projects/project/targets/target/host-sets.js +++ b/ui/core/app/routes/scopes/scope/projects/project/targets/target/host-sets.js @@ -4,6 +4,7 @@ import { action } from '@ember/object'; import { inject as service } from '@ember/service'; import loading from 'ember-loading/decorator'; import { confirm } from '../../../../../../../decorators/confirm'; +import { notifySuccess, notifyError } from '../../../../../../../decorators/notify'; export default class ScopesScopeProjectsProjectTargetsTargetHostSetsRoute extends Route { // =services @@ -57,14 +58,10 @@ export default class ScopesScopeProjectsProjectTargetsTargetHostSetsRoute extend @action @loading @confirm('questions.remove-confirm') + @notifyError(({ message }) => message, { catch: true }) + @notifySuccess('notifications.remove-success') async removeHostSet(target, hostSet) { - try { - await target.removeHostSet(hostSet.id); - this.refresh(); - this.notify.success(this.intl.t('notifications.remove-success')); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } + await target.removeHostSet(hostSet.id); + this.refresh(); } } diff --git a/ui/core/app/routes/scopes/scope/roles.js b/ui/core/app/routes/scopes/scope/roles.js index 99cf3061c..fd6991171 100644 --- a/ui/core/app/routes/scopes/scope/roles.js +++ b/ui/core/app/routes/scopes/scope/roles.js @@ -3,6 +3,7 @@ import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import loading from 'ember-loading/decorator'; import { confirm } from '../../../decorators/confirm'; +import { notifySuccess, notifyError } from '../../../decorators/notify'; export default class ScopesScopeRolesRoute extends Route { // =services @@ -47,20 +48,12 @@ export default class ScopesScopeRolesRoute extends Route { */ @action @loading + @notifyError(({ message }) => message) + @notifySuccess(({ isNew }) => isNew ? 'notifications.create-success' : 'notifications.save-success') async save(role) { - const { isNew } = role; - try { - await role.save(); - await this.transitionTo('scopes.scope.roles.role', role); - this.refresh(); - this.notify.success( - this.intl.t(isNew ? 'notifications.create-success' : 'notifications.save-success') - ); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - throw error; - } + await role.save(); + await this.transitionTo('scopes.scope.roles.role', role); + this.refresh(); } /** @@ -70,15 +63,11 @@ export default class ScopesScopeRolesRoute extends Route { @action @loading @confirm('questions.delete-confirm') + @notifyError(({ message }) => message, { catch: true }) + @notifySuccess('notifications.delete-success') async delete(role) { - try { - await role.destroyRecord(); - await this.replaceWith('scopes.scope.roles'); - this.refresh(); - this.notify.success(this.intl.t('notifications.delete-success')); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } + await role.destroyRecord(); + await this.replaceWith('scopes.scope.roles'); + this.refresh(); } } diff --git a/ui/core/app/routes/scopes/scope/roles/role/add-principals.js b/ui/core/app/routes/scopes/scope/roles/role/add-principals.js index 6d0bb1d46..861ceb1c5 100644 --- a/ui/core/app/routes/scopes/scope/roles/role/add-principals.js +++ b/ui/core/app/routes/scopes/scope/roles/role/add-principals.js @@ -3,6 +3,7 @@ import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import { hash } from 'rsvp'; import loading from 'ember-loading/decorator'; +import { notifySuccess, notifyError } from '../../../../../decorators/notify'; export default class ScopesScopeRolesRoleAddPrincipalsRoute extends Route { // =services @@ -59,15 +60,11 @@ export default class ScopesScopeRolesRoleAddPrincipalsRoute extends Route { */ @action @loading + @notifyError(({ message }) => message, { catch: true }) + @notifySuccess('notifications.add-success') async addPrincipals(role, principalIDs) { - try { - await role.addPrincipals(principalIDs); - this.replaceWith('scopes.scope.roles.role.principals'); - this.notify.success(this.intl.t('notifications.add-success')); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } + await role.addPrincipals(principalIDs); + this.replaceWith('scopes.scope.roles.role.principals'); } /** diff --git a/ui/core/app/routes/scopes/scope/roles/role/grants.js b/ui/core/app/routes/scopes/scope/roles/role/grants.js index d09901515..af56f1990 100644 --- a/ui/core/app/routes/scopes/scope/roles/role/grants.js +++ b/ui/core/app/routes/scopes/scope/roles/role/grants.js @@ -2,6 +2,7 @@ import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import loading from 'ember-loading/decorator'; +import { notifySuccess, notifyError } from '../../../../../decorators/notify'; export default class ScopesScopeRolesRoleGrantsRoute extends Route { // =services @@ -56,13 +57,9 @@ export default class ScopesScopeRolesRoleGrantsRoute extends Route { */ @action @loading + @notifyError(({ message }) => message, { catch: true }) + @notifySuccess('notifications.save-success') async save(role) { - try { await role.saveGrants(); - this.notify.success(this.intl.t('notifications.save-success')); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } } } diff --git a/ui/core/app/routes/scopes/scope/roles/role/principals.js b/ui/core/app/routes/scopes/scope/roles/role/principals.js index dc6b27677..1bc8e3969 100644 --- a/ui/core/app/routes/scopes/scope/roles/role/principals.js +++ b/ui/core/app/routes/scopes/scope/roles/role/principals.js @@ -4,6 +4,7 @@ import { action } from '@ember/object'; import { inject as service } from '@ember/service'; import loading from 'ember-loading/decorator'; import { confirm } from '../../../../../decorators/confirm'; +import { notifySuccess, notifyError } from '../../../../../decorators/notify'; export default class ScopesScopeRolesRolePrincipalsRoute extends Route { // =services @@ -67,16 +68,12 @@ export default class ScopesScopeRolesRolePrincipalsRoute extends Route { @action @loading @confirm('questions.remove-confirm') + @notifyError(({ message }) => message, { catch: true }) + @notifySuccess('notifications.remove-success') async removePrincipal(principal) { - try { - const role = this.modelFor('scopes.scope.roles.role'); - await role.removePrincipal(principal.id); - this.refresh(); - this.notify.success(this.intl.t('notifications.remove-success')); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } + const role = this.modelFor('scopes.scope.roles.role'); + await role.removePrincipal(principal.id); + this.refresh(); } } diff --git a/ui/core/app/routes/scopes/scope/users.js b/ui/core/app/routes/scopes/scope/users.js index 5cb1f0d4e..d65f2d35f 100644 --- a/ui/core/app/routes/scopes/scope/users.js +++ b/ui/core/app/routes/scopes/scope/users.js @@ -3,6 +3,7 @@ import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import loading from 'ember-loading/decorator'; import { confirm } from '../../../decorators/confirm'; +import { notifySuccess, notifyError } from '../../../decorators/notify'; export default class ScopesScopeUsersRoute extends Route { // =services @@ -48,20 +49,12 @@ export default class ScopesScopeUsersRoute extends Route { */ @action @loading + @notifyError(({ message }) => message) + @notifySuccess(({ isNew }) => isNew ? 'notifications.create-success' : 'notifications.save-success') async save(user) { - const { isNew } = user; - try { - await user.save(); - await this.transitionTo('scopes.scope.users.user', user); - this.refresh(); - this.notify.success( - this.intl.t(isNew ? 'notifications.create-success' : 'notifications.save-success') - ); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - throw error; - } + await user.save(); + await this.transitionTo('scopes.scope.users.user', user); + this.refresh(); } /** @@ -71,15 +64,11 @@ export default class ScopesScopeUsersRoute extends Route { @action @loading @confirm('questions.delete-confirm') + @notifyError(({ message }) => message, { catch: true }) + @notifySuccess('notifications.delete-success') async delete(user) { - try { - await user.destroyRecord(); - await this.replaceWith('scopes.scope.users'); - this.refresh(); - this.notify.success(this.intl.t('notifications.delete-success')); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } + await user.destroyRecord(); + await this.replaceWith('scopes.scope.users'); + this.refresh(); } } diff --git a/ui/core/app/routes/scopes/scope/users/user/accounts.js b/ui/core/app/routes/scopes/scope/users/user/accounts.js index cb00ca24e..4408bdd5c 100644 --- a/ui/core/app/routes/scopes/scope/users/user/accounts.js +++ b/ui/core/app/routes/scopes/scope/users/user/accounts.js @@ -3,6 +3,7 @@ import { action } from '@ember/object'; import { inject as service } from '@ember/service'; import { hash, all } from 'rsvp'; import loading from 'ember-loading/decorator'; +import { notifySuccess, notifyError } from '../../../../../decorators/notify'; export default class ScopesScopeUsersUserAccountsRoute extends Route { // =services @@ -38,14 +39,10 @@ export default class ScopesScopeUsersUserAccountsRoute extends Route { */ @action @loading + @notifyError(({ message }) => message, { catch: true }) + @notifySuccess('notifications.remove-success') async removeAccount(user, account) { - try { - await user.removeAccount(account.id); - await this.refresh(); - this.notify.success(this.intl.t('notifications.remove-success')); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } + await user.removeAccount(account.id); + await this.refresh(); } } diff --git a/ui/core/app/routes/scopes/scope/users/user/add-accounts.js b/ui/core/app/routes/scopes/scope/users/user/add-accounts.js index bdb7b0e9c..3a1e22f4e 100644 --- a/ui/core/app/routes/scopes/scope/users/user/add-accounts.js +++ b/ui/core/app/routes/scopes/scope/users/user/add-accounts.js @@ -3,6 +3,7 @@ import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import { all } from 'rsvp'; import loading from 'ember-loading/decorator'; +import { notifySuccess, notifyError } from '../../../../../decorators/notify'; export default class ScopesScopeUsersUserAddAccountsRoute extends Route { // =services @@ -69,15 +70,11 @@ export default class ScopesScopeUsersUserAddAccountsRoute extends Route { */ @action @loading + @notifyError(({ message }) => message, { catch: true }) + @notifySuccess('notifications.add-success') async addAccounts(user, accountIDs) { - try { - await user.addAccounts(accountIDs); - await this.replaceWith('scopes.scope.users.user.accounts'); - this.notify.success(this.intl.t('notifications.add-success')); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } + await user.addAccounts(accountIDs); + await this.replaceWith('scopes.scope.users.user.accounts'); } /**