diff --git a/ui/app/components/job-page/parts/evaluations.js b/ui/app/components/job-page/parts/evaluations.js deleted file mode 100644 index 33f6054a7..000000000 --- a/ui/app/components/job-page/parts/evaluations.js +++ /dev/null @@ -1,12 +0,0 @@ -import Component from '@ember/component'; -import { computed } from '@ember/object'; - -export default Component.extend({ - job: null, - - classNames: ['boxed-section'], - - sortedEvaluations: computed('job.evaluations.@each.modifyIndex', function() { - return (this.get('job.evaluations') || []).sortBy('modifyIndex').reverse(); - }), -}); diff --git a/ui/app/controllers/jobs/job/evaluations.js b/ui/app/controllers/jobs/job/evaluations.js new file mode 100644 index 000000000..f79228e7f --- /dev/null +++ b/ui/app/controllers/jobs/job/evaluations.js @@ -0,0 +1,24 @@ +import { alias } from '@ember/object/computed'; +import Controller, { inject as controller } from '@ember/controller'; +import WithNamespaceResetting from 'nomad-ui/mixins/with-namespace-resetting'; +import Sortable from 'nomad-ui/mixins/sortable'; + +export default Controller.extend(WithNamespaceResetting, Sortable, { + jobController: controller('jobs.job'), + + queryParams: { + sortProperty: 'sort', + sortDescending: 'desc', + }, + + sortProperty: 'modifyIndex', + sortDescending: true, + + job: alias('model'), + evaluations: alias('model.evaluations'), + + breadcrumbs: alias('jobController.breadcrumbs'), + + listToSort: alias('evaluations'), + sortedEvaluations: alias('listSorted'), +}); diff --git a/ui/app/router.js b/ui/app/router.js index 494a2173d..ce0a1bc74 100644 --- a/ui/app/router.js +++ b/ui/app/router.js @@ -13,6 +13,7 @@ Router.map(function() { this.route('definition'); this.route('versions'); this.route('deployments'); + this.route('evaluations'); }); }); diff --git a/ui/app/routes/jobs/job/evaluations.js b/ui/app/routes/jobs/job/evaluations.js new file mode 100644 index 000000000..640809db3 --- /dev/null +++ b/ui/app/routes/jobs/job/evaluations.js @@ -0,0 +1,19 @@ +import Route from '@ember/routing/route'; +import { collect } from '@ember/object/computed'; +import { watchRelationship } from 'nomad-ui/utils/properties/watch'; +import WithWatchers from 'nomad-ui/mixins/with-watchers'; + +export default Route.extend(WithWatchers, { + model() { + const job = this.modelFor('jobs.job'); + return job.get('evaluations').then(() => job); + }, + + startWatchers(controller, model) { + controller.set('watchEvaluations', this.get('watchEvaluations').perform(model)); + }, + + watchEvaluations: watchRelationship('evaluations'), + + watchers: collect('watchEvaluations'), +}); diff --git a/ui/app/templates/components/job-page/batch.hbs b/ui/app/templates/components/job-page/batch.hbs index d92573add..1ea0cedf9 100644 --- a/ui/app/templates/components/job-page/batch.hbs +++ b/ui/app/templates/components/job-page/batch.hbs @@ -29,6 +29,4 @@ sortProperty=sortProperty sortDescending=sortDescending gotoTaskGroup=gotoTaskGroup}} - - {{job-page/parts/evaluations job=job}} {{/job-page/parts/body}} diff --git a/ui/app/templates/components/job-page/parameterized-child.hbs b/ui/app/templates/components/job-page/parameterized-child.hbs index cd3d18f0b..d9104b571 100644 --- a/ui/app/templates/components/job-page/parameterized-child.hbs +++ b/ui/app/templates/components/job-page/parameterized-child.hbs @@ -36,8 +36,6 @@ sortDescending=sortDescending gotoTaskGroup=gotoTaskGroup}} - {{job-page/parts/evaluations job=job}} -
Payload
diff --git a/ui/app/templates/components/job-page/parts/evaluations.hbs b/ui/app/templates/components/job-page/parts/evaluations.hbs deleted file mode 100644 index f49a6f10b..000000000 --- a/ui/app/templates/components/job-page/parts/evaluations.hbs +++ /dev/null @@ -1,38 +0,0 @@ -
- Evaluations -
-
- {{#if sortedEvaluations.length}} - {{#list-table source=sortedEvaluations as |t|}} - {{#t.head}} - ID - Priority - Triggered By - Status - Placement Failures - {{/t.head}} - {{#t.body as |row|}} - - {{row.model.shortId}} - {{row.model.priority}} - {{row.model.triggeredBy}} - {{row.model.status}} - - {{#if (eq row.model.status "blocked")}} - N/A - In Progress - {{else if row.model.hasPlacementFailures}} - True - {{else}} - False - {{/if}} - - - {{/t.body}} - {{/list-table}} - {{else}} -
-

No Evaluations

-

This is most likely due to garbage collection.

-
- {{/if}} -
diff --git a/ui/app/templates/components/job-page/periodic-child.hbs b/ui/app/templates/components/job-page/periodic-child.hbs index 946ce2579..117a86679 100644 --- a/ui/app/templates/components/job-page/periodic-child.hbs +++ b/ui/app/templates/components/job-page/periodic-child.hbs @@ -35,6 +35,4 @@ sortProperty=sortProperty sortDescending=sortDescending gotoTaskGroup=gotoTaskGroup}} - - {{job-page/parts/evaluations job=job}} {{/job-page/parts/body}} diff --git a/ui/app/templates/components/job-page/service.hbs b/ui/app/templates/components/job-page/service.hbs index 80e2ab721..b724b7ab5 100644 --- a/ui/app/templates/components/job-page/service.hbs +++ b/ui/app/templates/components/job-page/service.hbs @@ -31,6 +31,4 @@ sortProperty=sortProperty sortDescending=sortDescending gotoTaskGroup=gotoTaskGroup}} - - {{job-page/parts/evaluations job=job}} {{/job-page/parts/body}} diff --git a/ui/app/templates/components/job-page/system.hbs b/ui/app/templates/components/job-page/system.hbs index d92573add..1ea0cedf9 100644 --- a/ui/app/templates/components/job-page/system.hbs +++ b/ui/app/templates/components/job-page/system.hbs @@ -29,6 +29,4 @@ sortProperty=sortProperty sortDescending=sortDescending gotoTaskGroup=gotoTaskGroup}} - - {{job-page/parts/evaluations job=job}} {{/job-page/parts/body}} diff --git a/ui/app/templates/components/list-table/sort-by.hbs b/ui/app/templates/components/list-table/sort-by.hbs index 64521875e..7e513143a 100644 --- a/ui/app/templates/components/list-table/sort-by.hbs +++ b/ui/app/templates/components/list-table/sort-by.hbs @@ -1,3 +1,5 @@ -{{#link-to (query-params sortProperty=prop sortDescending=shouldSortDescending)}} +{{#link-to + (query-params sortProperty=prop sortDescending=shouldSortDescending) + data-test-sort-by=prop}} {{yield}} {{/link-to}} diff --git a/ui/app/templates/jobs/job/evaluations.hbs b/ui/app/templates/jobs/job/evaluations.hbs new file mode 100644 index 000000000..7b46c0c25 --- /dev/null +++ b/ui/app/templates/jobs/job/evaluations.hbs @@ -0,0 +1,56 @@ +{{#global-header class="page-header"}} + {{#each breadcrumbs as |breadcrumb index|}} + + {{/each}} +{{/global-header}} +{{#gutter-menu class="page-body" onNamespaceChange=(action "gotoJobs")}} + {{partial "jobs/job/subnav"}} +
+
+
+ Evaluations +
+
+ {{#if sortedEvaluations.length}} + {{#list-table + source=sortedEvaluations + sortProperty=sortProperty + sortDescending=sortDescending as |t|}} + {{#t.head}} + ID + {{#t.sort-by prop="priority"}}Priority{{/t.sort-by}} + {{#t.sort-by prop="triggeredBy"}}Triggered By{{/t.sort-by}} + {{#t.sort-by prop="status"}}Status{{/t.sort-by}} + {{#t.sort-by prop="hasPlacementFailures"}}Placement Failures{{/t.sort-by}} + {{/t.head}} + {{#t.body as |row|}} + + {{row.model.shortId}} + {{row.model.priority}} + {{row.model.triggeredBy}} + {{row.model.status}} + + {{#if (eq row.model.status "blocked")}} + N/A - In Progress + {{else if row.model.hasPlacementFailures}} + True + {{else}} + False + {{/if}} + + + {{/t.body}} + {{/list-table}} + {{else}} +
+

No Evaluations

+

This is most likely due to garbage collection.

+
+ {{/if}} +
+
+
+{{/gutter-menu}} + diff --git a/ui/app/templates/jobs/job/subnav.hbs b/ui/app/templates/jobs/job/subnav.hbs index e4ef97f80..257072649 100644 --- a/ui/app/templates/jobs/job/subnav.hbs +++ b/ui/app/templates/jobs/job/subnav.hbs @@ -6,5 +6,6 @@ {{#if job.supportsDeployments}}
  • {{#link-to "jobs.job.deployments" job activeClass="is-active"}}Deployments{{/link-to}}
  • {{/if}} +
  • {{#link-to "jobs.job.evaluations" job activeClass="is-active"}}Evaluations{{/link-to}}
  • diff --git a/ui/tests/acceptance/job-evaluations-test.js b/ui/tests/acceptance/job-evaluations-test.js new file mode 100644 index 000000000..7fe187f5e --- /dev/null +++ b/ui/tests/acceptance/job-evaluations-test.js @@ -0,0 +1,56 @@ +import { test } from 'qunit'; +import { findAll, click } from 'ember-native-dom-helpers'; +import moduleForAcceptance from 'nomad-ui/tests/helpers/module-for-acceptance'; + +let job; +let evaluations; + +moduleForAcceptance('Acceptance | job evaluations', { + beforeEach() { + job = server.create('job', { noFailedPlacements: true, createAllocations: false }); + evaluations = server.db.evaluations.where({ jobId: job.id }); + + visit(`/jobs/${job.id}/evaluations`); + }, +}); + +test('lists all evaluations for the job', function(assert) { + const evaluationRows = findAll('[data-test-evaluation]'); + assert.equal(evaluationRows.length, evaluations.length, 'All evaluations are listed'); + + evaluations + .sortBy('modifyIndex') + .reverse() + .forEach((evaluation, index) => { + const shortId = evaluation.id.split('-')[0]; + assert.equal( + evaluationRows[index].querySelector('[data-test-id]').textContent.trim(), + shortId, + `Evaluation ${index} is ${shortId}` + ); + }); +}); + +test('evaluations table is sortable', function(assert) { + click('[data-test-sort-by="priority"]'); + + andThen(() => { + assert.equal( + currentURL(), + `/jobs/${job.id}/evaluations?sort=priority`, + 'the URL persists the sort parameter' + ); + const evaluationRows = findAll('[data-test-evaluation]'); + evaluations + .sortBy('priority') + .reverse() + .forEach((evaluation, index) => { + const shortId = evaluation.id.split('-')[0]; + assert.equal( + evaluationRows[index].querySelector('[data-test-id]').textContent.trim(), + shortId, + `Evaluation ${index} is ${shortId} with priority ${evaluation.priority}` + ); + }); + }); +}); diff --git a/ui/tests/integration/job-page/parts/evaluations-test.js b/ui/tests/integration/job-page/parts/evaluations-test.js deleted file mode 100644 index 982c1d294..000000000 --- a/ui/tests/integration/job-page/parts/evaluations-test.js +++ /dev/null @@ -1,65 +0,0 @@ -import { run } from '@ember/runloop'; -import { getOwner } from '@ember/application'; -import { test, moduleForComponent } from 'ember-qunit'; -import { findAll } from 'ember-native-dom-helpers'; -import wait from 'ember-test-helpers/wait'; -import hbs from 'htmlbars-inline-precompile'; -import { startMirage } from 'nomad-ui/initializers/ember-cli-mirage'; - -moduleForComponent( - 'job-page/parts/evaluations', - 'Integration | Component | job-page/parts/evaluations', - { - integration: true, - beforeEach() { - window.localStorage.clear(); - this.store = getOwner(this).lookup('service:store'); - this.server = startMirage(); - this.server.create('namespace'); - }, - afterEach() { - this.server.shutdown(); - window.localStorage.clear(); - }, - } -); - -test('lists all evaluations for the job', function(assert) { - let job; - - this.server.create('job', { noFailedPlacements: true, createAllocations: false }); - this.store.findAll('job'); - - return wait().then(() => { - run(() => { - job = this.store.peekAll('job').get('firstObject'); - }); - - this.setProperties({ job }); - - this.render(hbs` - {{job-page/parts/evaluations job=job}} - `); - - return wait().then(() => { - const evaluationRows = findAll('[data-test-evaluation]'); - assert.equal( - evaluationRows.length, - job.get('evaluations.length'), - 'All evaluations are listed' - ); - - job - .get('evaluations') - .sortBy('modifyIndex') - .reverse() - .forEach((evaluation, index) => { - assert.equal( - evaluationRows[index].querySelector('[data-test-id]').textContent.trim(), - evaluation.get('shortId'), - `Evaluation ${index} is ${evaluation.get('shortId')}` - ); - }); - }); - }); -});