Move the bulk of the new job page into a new job editor component

This commit is contained in:
Michael Lange 2018-08-21 12:07:45 -07:00
parent 55fca36bea
commit b3c2538c68
4 changed files with 167 additions and 156 deletions

View file

@ -0,0 +1,70 @@
import Component from '@ember/component';
import { inject as service } from '@ember/service';
import { computed } from '@ember/object';
import { task } from 'ember-concurrency';
import messageFromAdapterError from 'nomad-ui/utils/message-from-adapter-error';
import localStorageProperty from 'nomad-ui/utils/properties/local-storage';
export default Component.extend({
store: service(),
job: null,
onSubmit() {},
parseError: null,
planError: null,
runError: null,
planOutput: null,
showPlanMessage: localStorageProperty('nomadMessageJobPlan', true),
showEditorMessage: localStorageProperty('nomadMessageJobEditor', true),
stage: computed('planOutput', function() {
return this.get('planOutput') ? 'plan' : 'editor';
}),
plan: task(function*() {
this.reset();
try {
yield this.get('job').parse();
} catch (err) {
const error = messageFromAdapterError(err) || 'Could not parse input';
this.set('parseError', error);
return;
}
try {
yield this.get('job').plan();
const plan = this.get('store').peekRecord('job-plan', this.get('job.id'));
this.set('planOutput', plan);
} catch (err) {
const error = messageFromAdapterError(err) || 'Could not plan job';
this.set('planError', error);
}
}).drop(),
submit: task(function*() {
try {
yield this.get('job').run();
const id = this.get('job.plainId');
const namespace = this.get('job.namespace.name') || 'default';
this.reset();
// Treat the job as ephemeral and only provide ID parts.
this.get('onSubmit')(id, namespace);
} catch (err) {
const error = messageFromAdapterError(err) || 'Could not submit job';
this.set('runError', error);
}
}),
reset() {
this.set('planOutput', null);
this.set('planError', null);
this.set('parseError', null);
},
});

View file

@ -1,69 +1,9 @@
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
import { computed } from '@ember/object';
import { task } from 'ember-concurrency';
import messageFromAdapterError from 'nomad-ui/utils/message-from-adapter-error';
import localStorageProperty from 'nomad-ui/utils/properties/local-storage';
export default Controller.extend({
store: service(),
parseError: null,
planError: null,
runError: null,
planOutput: null,
showPlanMessage: localStorageProperty('nomadMessageJobPlan', true),
showEditorMessage: localStorageProperty('nomadMessageJobEditor', true),
stage: computed('planOutput', function() {
return this.get('planOutput') ? 'plan' : 'editor';
}),
plan: task(function*() {
this.reset();
try {
yield this.get('model').parse();
} catch (err) {
const error = messageFromAdapterError(err) || 'Could not parse input';
this.set('parseError', error);
return;
}
try {
yield this.get('model').plan();
const plan = this.get('store').peekRecord('job-plan', this.get('model.id'));
this.set('planOutput', plan);
} catch (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) {
const error = messageFromAdapterError(err) || 'Could not submit job';
this.set('runError', error);
}
}),
reset() {
this.set('planOutput', null);
this.set('planError', null);
this.set('parseError', null);
onSubmit(id, namespace) {
this.transitionToRoute('jobs.job', id, {
queryParams: { jobNamespace: namespace },
});
},
});

View file

@ -0,0 +1,92 @@
{{#if parseError}}
<div data-test-parse-error class="notification is-danger">
<h3 class="title is-4" data-test-parse-error-title>Parse Error</h3>
<p data-test-parse-error-message>{{parseError}}</p>
</div>
{{/if}}
{{#if planError}}
<div data-test-plan-error class="notification is-danger">
<h3 class="title is-4" data-test-plan-error-title>Plan Error</h3>
<p data-test-plan-error-message>{{planError}}</p>
</div>
{{/if}}
{{#if runError}}
<div data-test-run-error class="notification is-danger">
<h3 class="title is-4" data-test-run-error-title>Run Error</h3>
<p data-test-run-error-message>{{runError}}</p>
</div>
{{/if}}
{{#if (eq stage "editor")}}
{{#if showEditorMessage}}
<div class="notification is-info">
<div class="columns">
<div class="column">
<h3 class="title is-4" data-test-editor-help-title>Run a Job</h3>
<p data-test-editor-help-message>Paste or author HCL or JSON to submit to your cluster. A plan will be requested before the job is submitted.</p>
</div>
<div class="column is-centered is-minimum">
<button class="button is-info" onclick={{toggle-action "showEditorMessage" this}} data-test-editor-help-dismiss>Okay</button>
</div>
</div>
</div>
{{/if}}
<div class="boxed-section">
<div class="boxed-section-head">
Job Definition
</div>
<div class="boxed-section-body is-full-bleed">
{{ivy-codemirror
data-test-editor
value=(or job._newDefinition jobSpec)
valueUpdated=(action (mut job._newDefinition))
options=(hash
mode="javascript"
theme="hashi"
tabSize=2
lineNumbers=true
)}}
</div>
</div>
<div class="content is-associative">
<button class="button is-primary {{if plan.isRunning "is-loading"}}" type="button" onclick={{perform plan}} disabled={{or plan.isRunning (not job._newDefinition)}} data-test-plan>Plan</button>
</div>
{{/if}}
{{#if (eq stage "plan")}}
{{#if showPlanMessage}}
<div class="notification is-info">
<div class="columns">
<div class="column">
<h3 class="title is-4" data-test-plan-help-title>Job Plan</h3>
<p data-test-plan-help-message>This is the impact running this job will have on your cluster.</p>
</div>
<div class="column is-centered is-minimum">
<button class="button is-info" onclick={{toggle-action "showPlanMessage" this}} data-test-plan-help-dismiss>Okay</button>
</div>
</div>
</div>
{{/if}}
<div class="boxed-section">
<div class="boxed-section-head">Job Plan</div>
<div class="boxed-section-body is-dark">
{{job-diff data-test-plan-output diff=planOutput.diff verbose=false}}
</div>
</div>
<div class="boxed-section {{if planOutput.failedTGAllocs "is-warning" "is-primary"}}" data-test-dry-run-message>
<div class="boxed-section-head" data-test-dry-run-title>Scheduler dry-run</div>
<div class="boxed-section-body" data-test-dry-run-body>
{{#if planOutput.failedTGAllocs}}
{{#each planOutput.failedTGAllocs as |placementFailure|}}
{{placement-failure failedTGAlloc=placementFailure}}
{{/each}}
{{else}}
All tasks successfully allocated.
{{/if}}
</div>
</div>
<div class="content is-associative">
<button class="button is-primary {{if submit.isRunning "is-loading"}}" type="button" onclick={{perform submit}} disabled={{submit.isRunning}} data-test-run>Run</button>
<button class="button is-light" type="button" onclick={{action reset}} data-test-cancel>Cancel</button>
</div>
{{/if}}

View file

@ -1,94 +1,3 @@
<section class="section">
{{#if parseError}}
<div data-test-parse-error class="notification is-danger">
<h3 class="title is-4" data-test-parse-error-title>Parse Error</h3>
<p data-test-parse-error-message>{{parseError}}</p>
</div>
{{/if}}
{{#if planError}}
<div data-test-plan-error class="notification is-danger">
<h3 class="title is-4" data-test-plan-error-title>Plan Error</h3>
<p data-test-plan-error-message>{{planError}}</p>
</div>
{{/if}}
{{#if runError}}
<div data-test-run-error class="notification is-danger">
<h3 class="title is-4" data-test-run-error-title>Run Error</h3>
<p data-test-run-error-message>{{runError}}</p>
</div>
{{/if}}
{{#if (eq stage "editor")}}
{{#if showEditorMessage}}
<div class="notification is-info">
<div class="columns">
<div class="column">
<h3 class="title is-4" data-test-editor-help-title>Run a Job</h3>
<p data-test-editor-help-message>Paste or author HCL or JSON to submit to your cluster. A plan will be requested before the job is submitted.</p>
</div>
<div class="column is-centered is-minimum">
<button class="button is-info" onclick={{toggle-action "showEditorMessage" this}} data-test-editor-help-dismiss>Okay</button>
</div>
</div>
</div>
{{/if}}
<div class="boxed-section">
<div class="boxed-section-head">
Job Definition
</div>
<div class="boxed-section-body is-full-bleed">
{{ivy-codemirror
data-test-editor
value=(or model._newDefinition jobSpec)
valueUpdated=(action (mut model._newDefinition))
options=(hash
mode="javascript"
theme="hashi"
tabSize=2
lineNumbers=true
)}}
</div>
</div>
<div class="content is-associative">
<button class="button is-primary {{if plan.isRunning "is-loading"}}" type="button" onclick={{perform plan}} disabled={{or plan.isRunning (not model._newDefinition)}} data-test-plan>Plan</button>
</div>
{{/if}}
{{#if (eq stage "plan")}}
{{#if showPlanMessage}}
<div class="notification is-info">
<div class="columns">
<div class="column">
<h3 class="title is-4" data-test-plan-help-title>Job Plan</h3>
<p data-test-plan-help-message>This is the impact running this job will have on your cluster.</p>
</div>
<div class="column is-centered is-minimum">
<button class="button is-info" onclick={{toggle-action "showPlanMessage" this}} data-test-plan-help-dismiss>Okay</button>
</div>
</div>
</div>
{{/if}}
<div class="boxed-section">
<div class="boxed-section-head">Job Plan</div>
<div class="boxed-section-body is-dark">
{{job-diff data-test-plan-output diff=planOutput.diff verbose=false}}
</div>
</div>
<div class="boxed-section {{if planOutput.failedTGAllocs "is-warning" "is-primary"}}" data-test-dry-run-message>
<div class="boxed-section-head" data-test-dry-run-title>Scheduler dry-run</div>
<div class="boxed-section-body" data-test-dry-run-body>
{{#if planOutput.failedTGAllocs}}
{{#each planOutput.failedTGAllocs as |placementFailure|}}
{{placement-failure failedTGAlloc=placementFailure}}
{{/each}}
{{else}}
All tasks successfully allocated.
{{/if}}
</div>
</div>
<div class="content is-associative">
<button class="button is-primary {{if submit.isRunning "is-loading"}}" type="button" onclick={{perform submit}} disabled={{submit.isRunning}} data-test-run>Run</button>
<button class="button is-light" type="button" onclick={{action reset}} data-test-cancel>Cancel</button>
</div>
{{/if}}
{{job-editor job=model onSubmit=(action onSubmit)}}
</section>