diff --git a/.changelog/24168.txt b/.changelog/24168.txt new file mode 100644 index 000000000000..08e5bf4008b7 --- /dev/null +++ b/.changelog/24168.txt @@ -0,0 +1,3 @@ +```release-note:improvement +ui: Add an Edit From Version button as an option when reverting from an older job version +``` diff --git a/ui/app/adapters/job.js b/ui/app/adapters/job.js index 1d195876cdd8..b863ac5079c7 100644 --- a/ui/app/adapters/job.js +++ b/ui/app/adapters/job.js @@ -25,11 +25,11 @@ export default class JobAdapter extends WatchableNamespaceIDs { return this.ajax(url, 'GET'); } - fetchRawSpecification(job) { + fetchRawSpecification(job, version) { const url = addToPath( this.urlForFindRecord(job.get('id'), 'job', null, 'submission'), '', - 'version=' + job.get('version') + 'version=' + (version || job.get('version')) ); return this.ajax(url, 'GET'); } diff --git a/ui/app/components/job-version.js b/ui/app/components/job-version.js index 31606f9844e1..bcb7228b0b87 100644 --- a/ui/app/components/job-version.js +++ b/ui/app/components/job-version.js @@ -3,6 +3,8 @@ * SPDX-License-Identifier: BUSL-1.1 */ +// @ts-check + import Component from '@glimmer/component'; import { action, computed } from '@ember/object'; import { alias } from '@ember/object/computed'; @@ -80,6 +82,11 @@ export default class JobVersion extends Component { this.isOpen = !this.isOpen; } + /** + * @type {'idle' | 'confirming'} + */ + @tracked revertButtonStatus = 'idle'; + @task(function* () { try { const versionBeforeReversion = this.version.get('job.version'); @@ -108,6 +115,26 @@ export default class JobVersion extends Component { }) revertTo; + @action async editFromVersion() { + try { + this.router.transitionTo( + 'jobs.job.definition', + this.version.get('job.idWithNamespace'), + { + queryParams: { + isEditing: true, + version: this.version.number, + }, + } + ); + } catch (e) { + this.args.handleError({ + level: 'danger', + title: 'Could Not Edit from Version', + }); + } + } + @action handleKeydown(event) { if (event.key === 'Escape') { diff --git a/ui/app/models/job.js b/ui/app/models/job.js index eee53d647284..cca22f8fcaac 100644 --- a/ui/app/models/job.js +++ b/ui/app/models/job.js @@ -541,8 +541,8 @@ export default class Job extends Model { return this.store.adapterFor('job').fetchRawDefinition(this); } - fetchRawSpecification() { - return this.store.adapterFor('job').fetchRawSpecification(this); + fetchRawSpecification(version) { + return this.store.adapterFor('job').fetchRawSpecification(this, version); } forcePeriodic() { diff --git a/ui/app/routes/jobs/job/definition.js b/ui/app/routes/jobs/job/definition.js index 886683d6bb47..bd670a9d6765 100644 --- a/ui/app/routes/jobs/job/definition.js +++ b/ui/app/routes/jobs/job/definition.js @@ -5,29 +5,58 @@ // @ts-check import Route from '@ember/routing/route'; - +import { inject as service } from '@ember/service'; /** * Route for fetching and displaying a job's definition and specification. */ export default class DefinitionRoute extends Route { + @service notifications; + + queryParams = { + version: { + refreshModel: true, + }, + }; + /** * Fetch the job's definition, specification, and variables from the API. * * @returns {Promise} A promise that resolves to an object containing the job, definition, format, * specification, variableFlags, and variableLiteral. */ - async model() { + async model({ version }) { + version = +version; // query parameter is a string; convert to number const job = this.modelFor('jobs.job'); if (!job) return; - const definition = await job.fetchRawDefinition(); + let definition; + + if (version) { + try { + const versionResponse = await job.getVersions(); + const versions = versionResponse.Versions; + definition = versions.findBy('Version', version); + if (!definition) { + throw new Error('Version not found'); + } + } catch (e) { + console.error('error fetching job version definition', e); + this.notifications.add({ + title: 'Error Fetching Job Version Definition', + message: `There was an error fetching the versions for this job: ${e.message}`, + color: 'critical', + }); + } + } else { + definition = await job.fetchRawDefinition(); + } let format = 'json'; // default to json in network request errors let specification; let variableFlags; let variableLiteral; try { - const specificationResponse = await job.fetchRawSpecification(); + const specificationResponse = await job.fetchRawSpecification(version); specification = specificationResponse?.Source ?? null; variableFlags = specificationResponse?.VariableFlags ?? null; variableLiteral = specificationResponse?.Variables ?? null; diff --git a/ui/app/templates/components/job-version.hbs b/ui/app/templates/components/job-version.hbs index 587bb18c978b..c56503db4a11 100644 --- a/ui/app/templates/components/job-version.hbs +++ b/ui/app/templates/components/job-version.hbs @@ -81,20 +81,55 @@
{{#unless this.isCurrent}} {{#if (can "run job" namespace=this.version.job.namespace)}} - + {{#if (eq this.revertButtonStatus 'idle')}} + + {{else if (eq this.revertButtonStatus 'confirming')}} + + Are you sure you want to revert to this version? + + + + + {{else if (eq this.revertButtonStatus 'running')}} + + {{/if}} {{else}}