From f29f4351f1ca7728ab3746e4487d520ddbb36004 Mon Sep 17 00:00:00 2001 From: Michael Lange Date: Wed, 15 Aug 2018 15:18:38 -0700 Subject: [PATCH] Error messages for job submit --- ui/app/controllers/jobs/run.js | 29 +++++++--- ui/app/templates/jobs/run.hbs | 61 +++++++++++++++------- ui/app/utils/message-from-adapter-error.js | 6 +++ ui/app/utils/properties/local-storage.js | 19 +++++++ 4 files changed, 89 insertions(+), 26 deletions(-) create mode 100644 ui/app/utils/message-from-adapter-error.js create mode 100644 ui/app/utils/properties/local-storage.js diff --git a/ui/app/controllers/jobs/run.js b/ui/app/controllers/jobs/run.js index 871f7ed4f..7e0d01f5e 100644 --- a/ui/app/controllers/jobs/run.js +++ b/ui/app/controllers/jobs/run.js @@ -1,46 +1,61 @@ import Controller from '@ember/controller'; import { computed } from '@ember/object'; import { task } from 'ember-concurrency'; -import { qpBuilder } from 'nomad-ui/utils/classes/query-params'; -import { next } from '@ember/runloop'; +import messageFromAdapterError from 'nomad-ui/utils/message-from-adapter-error'; +import localStorageProperty from 'nomad-ui/utils/properties/local-storage'; export default Controller.extend({ + parseError: null, + planError: null, + runError: null, + + showPlanMessage: localStorageProperty('nomadMessageJobPlan', true), + showEditorMessage: localStorageProperty('nomadMessageJobEditor', true), + stage: computed('planOutput', function() { return this.get('planOutput') ? 'plan' : 'editor'; }), plan: task(function*() { - this.cancel(); + this.reset(); try { yield this.get('model').parse(); } catch (err) { - this.set('parseError', err); + const error = messageFromAdapterError(err) || 'Could not parse input'; + this.set('parseError', error); + return; } try { const planOutput = yield this.get('model').plan(); this.set('planOutput', planOutput); } catch (err) { - this.set('planError', err); + const error = messageFromAdapterError(err) || 'Could not plan job'; + this.set('planError', error); } }).drop(), submit: task(function*() { try { yield this.get('model').run(); + const id = this.get('model.plainId'); const namespace = this.get('model.namespace.name') || 'default'; + + this.reset(); + // navigate to the new job page this.transitionToRoute('jobs.job', id, { queryParams: { jobNamespace: namespace }, }); } catch (err) { - this.set('runError', err); + const error = messageFromAdapterError(err) || 'Could not submit job'; + this.set('runError', error); } }), - cancel() { + reset() { this.set('planOutput', null); this.set('planError', null); this.set('parseError', null); diff --git a/ui/app/templates/jobs/run.hbs b/ui/app/templates/jobs/run.hbs index 953ea855f..d27ee5f07 100644 --- a/ui/app/templates/jobs/run.hbs +++ b/ui/app/templates/jobs/run.hbs @@ -1,16 +1,37 @@
+ {{#if parseError}} +
+

Parse Error

+

{{parseError}}

+
+ {{/if}} + {{#if planError}} +
+

Plan Error

+

{{planError}}

+
+ {{/if}} + {{#if runError}} +
+

Run Error

+

{{runError}}

+
+ {{/if}} + {{#if (eq stage "editor")}} -
-
-
-

Run a Job

-

Paste or author HCL or JSON to submit to your cluster. A plan will be requested before the job is submitted.

-
-
- + {{#if showEditorMessage}} +
+
+
+

Run a Job

+

Paste or author HCL or JSON to submit to your cluster. A plan will be requested before the job is submitted.

+
+
+ +
-
+ {{/if}}
Job Definition @@ -33,17 +54,19 @@ {{/if}} {{#if (eq stage "plan")}} -
-
-
-

Job Plan

-

This is the impact running this job will have on your cluster.

-
-
- + {{#if showPlanMessage}} +
+
+
+

Job Plan

+

This is the impact running this job will have on your cluster.

+
+
+ +
-
+ {{/if}}
Job Plan
@@ -52,7 +75,7 @@
- +
{{/if}}
diff --git a/ui/app/utils/message-from-adapter-error.js b/ui/app/utils/message-from-adapter-error.js new file mode 100644 index 000000000..2b1ce864b --- /dev/null +++ b/ui/app/utils/message-from-adapter-error.js @@ -0,0 +1,6 @@ +// Returns a single string based on the response the adapter received +export default function messageFromAdapterError(error) { + if (error.errors) { + return error.errors.mapBy('detail').join('\n\n'); + } +} diff --git a/ui/app/utils/properties/local-storage.js b/ui/app/utils/properties/local-storage.js new file mode 100644 index 000000000..5049ed27c --- /dev/null +++ b/ui/app/utils/properties/local-storage.js @@ -0,0 +1,19 @@ +import { computed } from '@ember/object'; + +// An Ember.Computed property that persists set values in localStorage +// and will attempt to get its initial value from localStorage before +// falling back to a default. +// +// ex. showTutorial: localStorageProperty('nomadTutorial', true), +export default function localStorageProperty(localStorageKey, defaultValue) { + return computed({ + get() { + const persistedValue = window.localStorage.getItem(localStorageKey); + return persistedValue ? JSON.parse(persistedValue) : defaultValue; + }, + set(key, value) { + window.localStorage.setItem(localStorageKey, JSON.stringify(value)); + return value; + }, + }); +}