diff --git a/ui/app/adapters/job.js b/ui/app/adapters/job.js index 384419b533e..b781bf8008c 100644 --- a/ui/app/adapters/job.js +++ b/ui/app/adapters/job.js @@ -27,10 +27,28 @@ export default class JobAdapter extends WatchableNamespaceIDs { * This method is still important for backwards compatibility with older job versions, as well as a fallback * for when fetching raw specification fails. * @param {import('../models/job').default} job + * @param {number} version */ - fetchRawDefinition(job) { - const url = this.urlForFindRecord(job.get('id'), 'job'); - return this.ajax(url, 'GET'); + async fetchRawDefinition(job, version) { + if (version == null) { + const url = this.urlForFindRecord(job.get('id'), 'job'); + return this.ajax(url, 'GET'); + } + + // For specific versions, we need to fetch from versions endpoint, + // and then find the specified version info from the response. + const versionsUrl = addToPath( + this.urlForFindRecord(job.get('id'), 'job', null, 'versions') + ); + + const response = await this.ajax(versionsUrl, 'GET'); + const versionInfo = response.Versions.find((v) => v.Version === version); + + if (!versionInfo) { + throw new Error(`Version ${version} not found`); + } + + return versionInfo; } /** diff --git a/ui/app/models/job.js b/ui/app/models/job.js index cca22f8fcaa..007c8a699b5 100644 --- a/ui/app/models/job.js +++ b/ui/app/models/job.js @@ -537,8 +537,8 @@ export default class Job extends Model { return undefined; } - fetchRawDefinition() { - return this.store.adapterFor('job').fetchRawDefinition(this); + fetchRawDefinition(version) { + return this.store.adapterFor('job').fetchRawDefinition(this, version); } fetchRawSpecification(version) { diff --git a/ui/app/routes/jobs/job/definition.js b/ui/app/routes/jobs/job/definition.js index 12a4a54c7f8..31dabec20ae 100644 --- a/ui/app/routes/jobs/job/definition.js +++ b/ui/app/routes/jobs/job/definition.js @@ -35,12 +35,7 @@ export default class DefinitionRoute extends Route { if (version !== undefined) { // Not (!version) because version can be 0 try { - const versionResponse = await job.getVersions(); - const versions = versionResponse.Versions; - definition = versions.findBy('Version', version); - if (!definition) { - throw new Error('Version not found'); - } + definition = await job.fetchRawDefinition(version); } catch (e) { console.error('error fetching job version definition', e); this.notifications.add({ diff --git a/ui/tests/unit/adapters/job-test.js b/ui/tests/unit/adapters/job-test.js index f8efbb26860..370573113c7 100644 --- a/ui/tests/unit/adapters/job-test.js +++ b/ui/tests/unit/adapters/job-test.js @@ -501,7 +501,64 @@ module('Unit | Adapter | Job', function (hooks) { assert.equal(request.method, 'GET'); }); - // TODO: test that version requests work for fetchRawDefinition + test('fetchRawDefinition handles version requests', async function (assert) { + assert.expect(5); + + const adapter = this.owner.lookup('adapter:job'); + const job = { + get: sinon.stub(), + }; + + job.get.withArgs('id').returns('["job-id"]'); + + const mockVersionResponse = { + Versions: [ + { Version: 1, JobID: 'job-id', JobModifyIndex: 100 }, + { Version: 2, JobID: 'job-id', JobModifyIndex: 200 }, + ], + }; + + // Stub ajax to return mock version data + const ajaxStub = sinon.stub(adapter, 'ajax'); + ajaxStub + .withArgs('/v1/job/job-id/versions', 'GET') + .resolves(mockVersionResponse); + + // Test fetching specific version + const result = await adapter.fetchRawDefinition(job, 2); + assert.equal(result.Version, 2, 'Returns correct version'); + assert.equal(result.JobModifyIndex, 200, 'Returns full version info'); + + // Test version not found + try { + await adapter.fetchRawDefinition(job, 999); + assert.ok(false, 'Should have thrown error'); + } catch (e) { + assert.equal( + e.message, + 'Version 999 not found', + 'Throws appropriate error' + ); + } + + // Test no version specified (current version) + ajaxStub + .withArgs('/v1/job/job-id', 'GET') + .resolves({ ID: 'job-id', Version: 2 }); + + const currentResult = await adapter.fetchRawDefinition(job); + + assert.equal( + ajaxStub.lastCall.args[0], + '/v1/job/job-id', + 'URL has no version query param' + ); + assert.equal( + currentResult.Version, + 2, + 'Returns current version when no version specified' + ); + }); test('forcePeriodic requests include the activeRegion', async function (assert) { const region = 'region-2'; @@ -688,6 +745,8 @@ module('Unit | Adapter | Job', function (hooks) { const job = { get: sinon.stub(), }; + job.get.withArgs('id').returns('["job-id"]'); + job.get.withArgs('version').returns(100); // Stub the ajax method to avoid making real API calls sinon.stub(adapter, 'ajax').callsFake(() => resolve({}));