diff --git a/ui/core/app/components/error/message.hbs b/ui/core/app/components/error/message.hbs new file mode 100644 index 000000000..7cc59f8c9 --- /dev/null +++ b/ui/core/app/components/error/message.hbs @@ -0,0 +1,4 @@ + + {{t this.description}} + {{t "actions.help"}} + \ No newline at end of file diff --git a/ui/core/app/components/error/message.js b/ui/core/app/components/error/message.js new file mode 100644 index 000000000..c949ba822 --- /dev/null +++ b/ui/core/app/components/error/message.js @@ -0,0 +1,104 @@ +import Message from 'rose/components/rose/message'; + +/* + * Helpful error booleans are attached based on the error status code: + * + * - 401 - `error.isUnauthenticated`: the session isn't authenticated + * - 403 - `error.isForbidden`: the session is authenticated but does not have + * permission to perform the requested action + * - 404 - `error.isNotFound`: the requested resource could not be found + * - 500 - `error.isServer`: an internal server error occurred + * + * For an unknown error state, i.e error state not matching to the above defined list: + * - unknown - `error.isUnknown`: an error occurred, but we don't know which or + * we don't distinguish it yet +*/ + +const status_messages = { + '401' : { + icon: 'disabled', + title: 'errors.titles.unauthenticated-error', + subtitle: 'errors.subtitles.unauthenticated-error', + description: 'errors.descriptions.unauthenticated-error', + }, + '403' : { + icon: 'disabled', + title: 'errors.titles.forbidden-error', + subtitle: 'errors.subtitles.forbidden-error', + description: 'errors.descriptions.forbidden-error', + }, + '404' : { + icon: 'help-circle-outline', + title: 'errors.titles.notfound-error', + subtitle: 'errors.subtitles.notfound-error', + description: 'errors.descriptions.notfound-error', + }, + '500' : { + icon: 'alert-circle-outline', + title: 'errors.titles.server-error', + subtitle: 'errors.subtitles.server-error', + description: 'errors.descriptions.server-error', + }, + 'unknown' : { + icon: 'alert-circle-outline', + title: 'errors.titles.unknown-error', + subtitle: 'errors.subtitles.unknown-error', + description: 'errors.descriptions.unknown-error', + } +}; + +export default class ErrorMessageComponent extends Message { + // =methods + + /** + * Returns an icon name based on error status. + * @return {string} + */ + get icon() { + return this.statusMessage.icon; + } + + /** + * Returns a message title based on error status. + * @return {string} + */ + get title() { + return this.statusMessage.title; + } + + /** + * Returns a message subtitle of error status prefixed with 'Error'. + * @return {string} + */ + get subtitle() { + return this.statusMessage.subtitle; + } + + /** + * Returns an error description based on error status. + * @return {string} + */ + get description() { + return this.statusMessage.description; + } + + /** + * Returns an route string for help text in message. + * @return {string} + */ + get helpRoute() { + return 'index' + } + + /** + * Returns an object with message details based on error status. + * Return message for `unkown` error when error status isn't part of + * predefined message set. + * @return {object} + */ + get statusMessage() { + let status = this.status; + if(!status_messages[status]) { status = 'unknown'; } + return status_messages[status]; + } +} \ No newline at end of file diff --git a/ui/core/app/routes/application.js b/ui/core/app/routes/application.js index 0a6c58271..1ee2b2ce2 100644 --- a/ui/core/app/routes/application.js +++ b/ui/core/app/routes/application.js @@ -50,8 +50,6 @@ export default class ApplicationRoute extends Route.extend( if (!Ember.testing) later(location, location.reload, 150); } - // =actions - /** * Delegates invalidation to the session service. */ diff --git a/ui/core/app/routes/scopes/scope.js b/ui/core/app/routes/scopes/scope.js index 820f7caf2..def116b51 100644 --- a/ui/core/app/routes/scopes/scope.js +++ b/ui/core/app/routes/scopes/scope.js @@ -33,4 +33,5 @@ export default class ScopesScopeRoute extends Route { ); }); } + } diff --git a/ui/core/app/templates/scopes/scope/error.hbs b/ui/core/app/templates/scopes/scope/error.hbs new file mode 100644 index 000000000..417feb0ea --- /dev/null +++ b/ui/core/app/templates/scopes/scope/error.hbs @@ -0,0 +1,6 @@ + + {{#each @model.errors as |error|}} + {{title error.status}} + + {{/each}} + \ No newline at end of file diff --git a/ui/core/mirage/config.js b/ui/core/mirage/config.js index 7f2ea5ba0..846da6b99 100644 --- a/ui/core/mirage/config.js +++ b/ui/core/mirage/config.js @@ -1,6 +1,6 @@ import config from '../config/environment'; import { authHandler, deauthHandler } from './route-handlers/auth'; -//import { Response } from 'miragejs'; +import { Response } from 'miragejs'; export default function() { @@ -288,4 +288,18 @@ export default function() { } return target.update(updatedAttrs); }); + + /* Uncomment the following line and the Response import above + * Then change the response code to simulate error responses. + * this.get('/scopes', () => new Response(505)); + * + * Update error payload to simulate specific error responses. + */ + // this.get('/host-catalogs', () => new Response(404)); + // this.get('/scopes/:id', () => new Response(505, {}, { + // errors: [{ + // status: 505, + // message: 'HTTP version not supported.', + // }] + // })); } diff --git a/ui/core/tests/integration/components/error/message-test.js b/ui/core/tests/integration/components/error/message-test.js new file mode 100644 index 000000000..811a0a138 --- /dev/null +++ b/ui/core/tests/integration/components/error/message-test.js @@ -0,0 +1,26 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; + +module('Integration | Component | error/message', function(hooks) { + setupRenderingTest(hooks); + + test('it renders', async function(assert) { + // Set any properties with this.set('myProperty', 'value'); + // Handle any actions with this.set('myAction', function(val) { ... }); + + await render(hbs``); + + assert.equal(this.element.textContent.trim(), ''); + + // Template block usage: + await render(hbs` + + template block text + + `); + + assert.equal(this.element.textContent.trim(), 'template block text'); + }); +}); diff --git a/ui/core/translations/en-us.yaml b/ui/core/translations/en-us.yaml index 301ac40a2..756b98b98 100644 --- a/ui/core/translations/en-us.yaml +++ b/ui/core/translations/en-us.yaml @@ -162,6 +162,12 @@ errors: server-error: Server error unknown-error: Something went wrong authentication-failed: Authentication Failed + subtitles: + unauthenticated-error: Error 401 + forbidden-error: Error 403 + notfound-error: Error 404 + server-error: Error 500 + unknown-error: Error descriptions: unauthenticated-error: You are not authenticated. Please authenticate and try again later. forbidden-error: You must be granted permissions to view this data. Ask your administrator if you think you should have access.