Job spec upload (#14747)
* Job spec upload by click or drag * pseudo-restrict formats * Changelog * Tweak to job spec upload to be above editor layer * Within the job-editor again tho * Beginning testcase cleanup * Test progression * refact: update codemirror fillin logic Co-authored-by: Jai Bhagat <jaybhagat841@gmail.com>
This commit is contained in:
parent
a0bdc67d6a
commit
6d5fe56fa1
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:improvement
|
||||||
|
ui: allow users to upload files by click or drag in the web ui
|
||||||
|
```
|
|
@ -49,7 +49,6 @@ export default class JobEditor extends Component {
|
||||||
planOutput = null;
|
planOutput = null;
|
||||||
|
|
||||||
@localStorageProperty('nomadMessageJobPlan', true) showPlanMessage;
|
@localStorageProperty('nomadMessageJobPlan', true) showPlanMessage;
|
||||||
@localStorageProperty('nomadMessageJobEditor', true) showEditorMessage;
|
|
||||||
|
|
||||||
@computed('planOutput')
|
@computed('planOutput')
|
||||||
get stage() {
|
get stage() {
|
||||||
|
@ -117,4 +116,14 @@ export default class JobEditor extends Component {
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@action uploadJobSpec(event) {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = () => {
|
||||||
|
this.updateCode(reader.result);
|
||||||
|
};
|
||||||
|
|
||||||
|
const [file] = event.target.files;
|
||||||
|
reader.readAsText(file);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,3 +129,25 @@ $dark-bright: lighten($dark, 15%);
|
||||||
background-color: $dark-2;
|
background-color: $dark-2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
header.run-job-header {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr auto;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
gap: 0 1rem;
|
||||||
|
& > h1 {
|
||||||
|
grid-column: -1 / 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-spec-upload {
|
||||||
|
.button {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -18,26 +18,16 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if (eq this.stage "editor")}}
|
{{#if (eq this.stage "editor")}}
|
||||||
{{#if (and this.showEditorMessage (eq this.context "new"))}}
|
|
||||||
<div class="notification is-info">
|
<header class="run-job-header">
|
||||||
<div class="columns">
|
<h1 class="title is-3">Run a job</h1>
|
||||||
<div class="column">
|
<p>Paste or author HCL or JSON to submit to your cluster. A plan will be requested before the job is submitted. You can also attach a job spec by uploading a job file or dragging & dropping a file to the editor.</p>
|
||||||
<h3 class="title is-4" data-test-editor-help-title>Run a Job</h3>
|
<label class="job-spec-upload">
|
||||||
<p data-test-editor-help-message>Paste or author HCL or JSON to submit
|
<input type="file" onchange={{action this.uploadJobSpec}} accept=".hcl,.json,.nomad" />
|
||||||
to your cluster. A plan will be requested before the job is
|
<span class="button">Upload a job spec file</span>
|
||||||
submitted.</p>
|
</label>
|
||||||
</div>
|
</header>
|
||||||
<div class="column is-centered is-minimum">
|
|
||||||
<button
|
|
||||||
class="button is-info"
|
|
||||||
onclick={{toggle-action "showEditorMessage" this}}
|
|
||||||
data-test-editor-help-dismiss
|
|
||||||
type="button"
|
|
||||||
>Okay</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
<div class="boxed-section">
|
<div class="boxed-section">
|
||||||
<div class="boxed-section-head">
|
<div class="boxed-section-head">
|
||||||
Job Definition
|
Job Definition
|
||||||
|
@ -63,6 +53,7 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content is-associative">
|
<div class="content is-associative">
|
||||||
<button
|
<button
|
||||||
class="button is-primary {{if this.plan.isRunning 'is-loading'}}"
|
class="button is-primary {{if this.plan.isRunning 'is-loading'}}"
|
||||||
|
|
|
@ -105,59 +105,29 @@ module('Integration | Component | job-editor', function (hooks) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const planJob = async (spec) => {
|
const planJob = async (spec) => {
|
||||||
await Editor.editor.fillIn(spec);
|
const cm = getCodeMirrorInstance(['data-test-editor']);
|
||||||
|
cm.setValue(spec);
|
||||||
await Editor.plan();
|
await Editor.plan();
|
||||||
};
|
};
|
||||||
|
|
||||||
test('the default state is an editor with an explanation popup', async function (assert) {
|
test('the default state is an editor with an explanation popup', async function (assert) {
|
||||||
assert.expect(3);
|
assert.expect(2);
|
||||||
|
|
||||||
const job = await this.store.createRecord('job');
|
const job = await this.store.createRecord('job');
|
||||||
|
|
||||||
await renderNewJob(this, job);
|
await renderNewJob(this, job);
|
||||||
assert.ok(
|
|
||||||
Editor.editorHelp.isPresent,
|
|
||||||
'Editor explanation popup is present'
|
|
||||||
);
|
|
||||||
assert.ok(Editor.editor.isPresent, 'Editor is present');
|
assert.ok(Editor.editor.isPresent, 'Editor is present');
|
||||||
|
|
||||||
await componentA11yAudit(this.element, assert);
|
await componentA11yAudit(this.element, assert);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('the explanation popup can be dismissed', async function (assert) {
|
|
||||||
const job = await this.store.createRecord('job');
|
|
||||||
|
|
||||||
await renderNewJob(this, job);
|
|
||||||
await Editor.editorHelp.dismiss();
|
|
||||||
assert.notOk(
|
|
||||||
Editor.editorHelp.isPresent,
|
|
||||||
'Editor explanation popup is gone'
|
|
||||||
);
|
|
||||||
assert.equal(
|
|
||||||
window.localStorage.nomadMessageJobEditor,
|
|
||||||
'false',
|
|
||||||
'Dismissal is persisted in localStorage'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('the explanation popup is not shown once the dismissal state is set in localStorage', async function (assert) {
|
|
||||||
window.localStorage.nomadMessageJobEditor = 'false';
|
|
||||||
|
|
||||||
const job = await this.store.createRecord('job');
|
|
||||||
|
|
||||||
await renderNewJob(this, job);
|
|
||||||
assert.notOk(
|
|
||||||
Editor.editorHelp.isPresent,
|
|
||||||
'Editor explanation popup is gone'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('submitting a json job skips the parse endpoint', async function (assert) {
|
test('submitting a json job skips the parse endpoint', async function (assert) {
|
||||||
const spec = jsonJob();
|
const spec = jsonJob();
|
||||||
const job = await this.store.createRecord('job');
|
const job = await this.store.createRecord('job');
|
||||||
|
|
||||||
await renderNewJob(this, job);
|
await renderNewJob(this, job);
|
||||||
await planJob(spec);
|
await planJob(spec);
|
||||||
|
console.log('wait');
|
||||||
const requests = this.server.pretender.handledRequests.mapBy('url');
|
const requests = this.server.pretender.handledRequests.mapBy('url');
|
||||||
assert.notOk(
|
assert.notOk(
|
||||||
requests.includes('/v1/jobs/parse'),
|
requests.includes('/v1/jobs/parse'),
|
||||||
|
|
|
@ -28,13 +28,6 @@ export default () => ({
|
||||||
dismiss: clickable('[data-test-plan-help-dismiss]'),
|
dismiss: clickable('[data-test-plan-help-dismiss]'),
|
||||||
},
|
},
|
||||||
|
|
||||||
editorHelp: {
|
|
||||||
isPresent: isPresent('[data-test-editor-help-title]'),
|
|
||||||
title: text('[data-test-editor-help-title]'),
|
|
||||||
message: text('[data-test-editor-help-message]'),
|
|
||||||
dismiss: clickable('[data-test-editor-help-dismiss]'),
|
|
||||||
},
|
|
||||||
|
|
||||||
editor: {
|
editor: {
|
||||||
isPresent: isPresent('[data-test-editor]'),
|
isPresent: isPresent('[data-test-editor]'),
|
||||||
contents: code('[data-test-editor]'),
|
contents: code('[data-test-editor]'),
|
||||||
|
|
Loading…
Reference in New Issue