08d97a19ca
* ui: Toggle for `read-only` view (#16279) * ui: model update for specification * style: add styling for select * style: add styling for select * refact: add spec to view * refact: update component API * test: refactor for new UI state * refact: clean conditional * refact: update component API for prop * chore: correct naming * chore: remove `fn` helper Co-authored-by: Phil Renaud <phil.renaud@hashicorp.com> * update `default` Mirage scenario (#16496) * chore: update mirage scenario: * ui: conditionally render toggle button (#16497) * chore: update css variable name (#16498) --------- Co-authored-by: Phil Renaud <phil.renaud@hashicorp.com> * ui: Display JSON view of variables associated to job specification (#16570) * chore: move fixture to util * chore: update tests: * ui: display variables table * chore: add mirage fixture (#16572) * ui: regex for job spec parse (#16668) * ui: remove variable table (#16670) * ui: notify user if specification has variables (#16671) * ui: regex for job spec parse * chore: deprecate variable references * chore: update mirage * ui: add notification * test: add test coverage for parse method (#16590) * refact: `JobEditor` reactive query parameters (#16710) * refact: add query parameter * refact: move toggle action to controller * ui: remove toggle behavior in `JobEditor` (#16711) * refact: rename logic for select * chore: instantiate qp in route * refact: uniform alerts (#16715) * style: buffer between alert and header * refact: extract alerts into a component * chore: update tests for qp * chore: defensive logic for app controller * refact: move `edit` state to controller (#16725) * refact: move edit state to controller * refact: handle edit state (#16731) * refact: handle edit state * ui: warning message (#16732) * ui: warning message * ui: enable editing of HCL vars in the UI (#16734) * enable editing of HCL vars * refact: default qp logic * refact: alert condition * refact: Pass `variables` as string (#16849) * ui: Toggle for `read-only` view (#16279) * ui: model update for specification * style: add styling for select * style: add styling for select * refact: add spec to view * refact: update component API * test: refactor for new UI state * refact: clean conditional * refact: update component API for prop * chore: correct naming * chore: remove `fn` helper Co-authored-by: Phil Renaud <phil.renaud@hashicorp.com> * update `default` Mirage scenario (#16496) * chore: update mirage scenario: * ui: conditionally render toggle button (#16497) * chore: update css variable name (#16498) --------- Co-authored-by: Phil Renaud <phil.renaud@hashicorp.com> * refact: `JobEditor` reactive query parameters (#16710) * refact: add query parameter * refact: move toggle action to controller * ui: remove toggle behavior in `JobEditor` (#16711) * refact: rename logic for select * chore: instantiate qp in route * refact: uniform alerts (#16715) * style: buffer between alert and header * refact: extract alerts into a component * chore: update tests for qp * chore: defensive logic for app controller * refact: move `edit` state to controller (#16725) * refact: move edit state to controller * refact: handle edit state (#16731) * refact: handle edit state * ui: warning message (#16732) * ui: warning message * ui: enable editing of HCL vars in the UI (#16734) * enable editing of HCL vars * refact: default qp logic * refact: alert condition * refact: variables as string * style: revert styling change --------- Co-authored-by: Phil Renaud <phil.renaud@hashicorp.com> * bug: correctly edit variables (#16989) * ui: visualize variables (#16987) * ui: fetchRawSpecification * refact: integrate new model method * test: fetchRaw unit * styling: enable height on cm * chore: update copy * feat: visual variables * chore: conditional render info txt * refact: add mirage endpoint * refact: update test for new schema * refact: job submit flow (#17015) * refact: job update logic * chore: remove dead code * bug: update `job.run` and `job.update` adapter methods (#17055) * refact: update adapter * chore: update api usage * styling: UX requests (#17064) * refact: update adapter * chore: update api usage * styling: disable toggle w text * styling: stick button * style: space out alerts * chore: autofocus on first editor * bug: dismiss alert * chore: add jsdoc and assertion check * chore: update mirage for Vercel (#17054) * chore: mirage logic for vercel deploy * chore: update test for mirage change * refact: API refactoring (#17083) * refact: udpate for req schema * refact: update for variable flags and literal * bug: visualize job model not derived state * chore: update copy * chore: fix incorrect copy * chore: deprecate variables derived state * chore: update copy * feat: enable toggle on edit * chore: prettify * refact: move conditional --------- Co-authored-by: Phil Renaud <phil.renaud@hashicorp.com>
202 lines
4.5 KiB
JavaScript
202 lines
4.5 KiB
JavaScript
/**
|
|
* Copyright (c) HashiCorp, Inc.
|
|
* SPDX-License-Identifier: MPL-2.0
|
|
*/
|
|
|
|
import Component from '@glimmer/component';
|
|
import { inject as service } from '@ember/service';
|
|
import { action } 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';
|
|
import { tracked } from '@glimmer/tracking';
|
|
|
|
export default class JobEditor extends Component {
|
|
@service config;
|
|
@service store;
|
|
|
|
@tracked error = null;
|
|
@tracked planOutput = null;
|
|
|
|
constructor() {
|
|
super(...arguments);
|
|
|
|
if (this.definition) {
|
|
this.setDefinitionOnModel();
|
|
}
|
|
|
|
if (this.args.variables) {
|
|
this.args.job.set(
|
|
'_newDefinitionVariables',
|
|
this.jsonToHcl(this.args.variables.flags).concat(
|
|
this.args.variables.literal
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
get isEditing() {
|
|
return ['new', 'edit'].includes(this.args.context);
|
|
}
|
|
|
|
@action
|
|
setDefinitionOnModel() {
|
|
this.args.job.set('_newDefinition', this.definition);
|
|
}
|
|
|
|
@action
|
|
edit() {
|
|
this.setDefinitionOnModel();
|
|
this.args.onToggleEdit(true);
|
|
}
|
|
|
|
@action
|
|
onCancel() {
|
|
this.args.onToggleEdit(false);
|
|
}
|
|
|
|
get stage() {
|
|
if (this.planOutput) return 'review';
|
|
if (this.isEditing) return 'edit';
|
|
else return 'read';
|
|
}
|
|
|
|
@localStorageProperty('nomadMessageJobPlan', true) shouldShowPlanMessage;
|
|
|
|
@action
|
|
dismissPlanMessage() {
|
|
this.shouldShowPlanMessage = false;
|
|
}
|
|
|
|
@(task(function* () {
|
|
this.reset();
|
|
|
|
try {
|
|
yield this.args.job.parse();
|
|
} catch (err) {
|
|
this.onError(err, 'parse', 'parse jobs');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const plan = yield this.args.job.plan();
|
|
this.planOutput = plan;
|
|
} catch (err) {
|
|
this.onError(err, 'plan', 'plan jobs');
|
|
}
|
|
}).drop())
|
|
plan;
|
|
|
|
@task(function* () {
|
|
try {
|
|
if (this.args.context === 'new') {
|
|
yield this.args.job.run();
|
|
} else {
|
|
yield this.args.job.update(this.args.format);
|
|
}
|
|
|
|
const id = this.args.job.plainId;
|
|
const namespace = this.args.job.belongsTo('namespace').id() || 'default';
|
|
|
|
this.reset();
|
|
|
|
// Treat the job as ephemeral and only provide ID parts.
|
|
this.args.onSubmit(id, namespace);
|
|
} catch (err) {
|
|
this.onError(err, 'run', 'submit jobs');
|
|
this.planOutput = null;
|
|
}
|
|
})
|
|
submit;
|
|
|
|
onError(err, type, actionMsg) {
|
|
const error = messageFromAdapterError(err, actionMsg);
|
|
this.error = { message: error, type };
|
|
this.scrollToError();
|
|
}
|
|
|
|
@action
|
|
reset() {
|
|
this.planOutput = null;
|
|
this.error = null;
|
|
}
|
|
|
|
scrollToError() {
|
|
if (!this.config.get('isTest')) {
|
|
window.scrollTo(0, 0);
|
|
}
|
|
}
|
|
|
|
@action
|
|
updateCode(value, _codemirror, type = 'job') {
|
|
if (!this.args.job.isDestroying && !this.args.job.isDestroyed) {
|
|
if (type === 'hclVariables') {
|
|
this.args.job.set('_newDefinitionVariables', value);
|
|
} else {
|
|
this.args.job.set('_newDefinition', value);
|
|
}
|
|
}
|
|
}
|
|
|
|
@action
|
|
uploadJobSpec(event) {
|
|
const reader = new FileReader();
|
|
reader.onload = () => {
|
|
this.updateCode(reader.result);
|
|
};
|
|
|
|
const [file] = event.target.files;
|
|
reader.readAsText(file);
|
|
}
|
|
|
|
get definition() {
|
|
if (this.args.view === 'full-definition') {
|
|
return JSON.stringify(this.args.definition, null, 2);
|
|
} else {
|
|
return this.args.specification;
|
|
}
|
|
}
|
|
|
|
jsonToHcl(obj) {
|
|
const hclLines = [];
|
|
|
|
for (const key in obj) {
|
|
const value = obj[key];
|
|
const hclValue = typeof value === 'string' ? `"${value}"` : value;
|
|
hclLines.push(`${key}=${hclValue}\n`);
|
|
}
|
|
|
|
return hclLines.join('\n');
|
|
}
|
|
|
|
get data() {
|
|
return {
|
|
cancelable: this.args.cancelable,
|
|
definition: this.definition,
|
|
format: this.args.format,
|
|
hasSpecification: !!this.args.specification,
|
|
hasVariables:
|
|
!!this.args.variables?.flags || !!this.args.variables?.literal,
|
|
job: this.args.job,
|
|
planOutput: this.planOutput,
|
|
shouldShowPlanMessage: this.shouldShowPlanMessage,
|
|
view: this.args.view,
|
|
};
|
|
}
|
|
|
|
get fns() {
|
|
return {
|
|
onCancel: this.onCancel,
|
|
onDismissPlanMessage: this.dismissPlanMessage,
|
|
onEdit: this.edit,
|
|
onPlan: this.plan,
|
|
onReset: this.reset,
|
|
onSaveAs: this.args.handleSaveAsTemplate,
|
|
onSubmit: this.submit,
|
|
onSelect: this.args.onSelect,
|
|
onUpdate: this.updateCode,
|
|
onUpload: this.uploadJobSpec,
|
|
};
|
|
}
|
|
}
|