diff --git a/ui/app/controllers/jobs.js b/ui/app/controllers/jobs.js index b924ed809..94b567ad0 100644 --- a/ui/app/controllers/jobs.js +++ b/ui/app/controllers/jobs.js @@ -13,20 +13,8 @@ export default Controller.extend({ // The namespace query param should act as an alias to the system active namespace. // But query param defaults can't be CPs: https://github.com/emberjs/ember.js/issues/9819 - syncNamespaceService: observer('jobNamespace', function() { - const newNamespace = this.get('jobNamespace'); - const currentNamespace = this.get('system.activeNamespace.id'); - const bothAreDefault = - (currentNamespace == undefined || currentNamespace === 'default') && - (newNamespace == undefined || newNamespace === 'default'); - - if (currentNamespace !== newNamespace && !bothAreDefault) { - this.set('system.activeNamespace', newNamespace); - run.next(() => { - this.send('refreshRoute'); - }); - } - }), + syncNamespaceService: forwardNamespace('jobNamespace', 'system.activeNamespace'), + syncNamespaceParam: forwardNamespace('system.activeNamespace', 'jobNamespace'), actions: { refreshRoute() { @@ -34,3 +22,20 @@ export default Controller.extend({ }, }, }); + +function forwardNamespace(source, destination) { + return observer(source, `${source}.id`, function() { + const newNamespace = this.get(`${source}.id`) || this.get(source); + const currentNamespace = this.get(`${destination}.id`) || this.get(destination); + const bothAreDefault = + (currentNamespace == undefined || currentNamespace === 'default') && + (newNamespace == undefined || newNamespace === 'default'); + + if (currentNamespace !== newNamespace && !bothAreDefault) { + this.set(destination, newNamespace); + run.next(() => { + this.send('refreshRoute'); + }); + } + }); +} diff --git a/ui/app/controllers/jobs/job/definition.js b/ui/app/controllers/jobs/job/definition.js index 14dd7214a..95144a831 100644 --- a/ui/app/controllers/jobs/job/definition.js +++ b/ui/app/controllers/jobs/job/definition.js @@ -1,8 +1,9 @@ import Ember from 'ember'; +import WithNamespaceResetting from 'nomad-ui/mixins/with-namespace-resetting'; const { Controller, computed, inject } = Ember; -export default Controller.extend({ +export default Controller.extend(WithNamespaceResetting, { jobController: inject.controller('jobs.job'), job: computed.alias('model.job'), diff --git a/ui/app/controllers/jobs/job/deployments.js b/ui/app/controllers/jobs/job/deployments.js index 97e3c9981..a8c6b1761 100644 --- a/ui/app/controllers/jobs/job/deployments.js +++ b/ui/app/controllers/jobs/job/deployments.js @@ -1,8 +1,9 @@ import Ember from 'ember'; +import WithNamespaceResetting from 'nomad-ui/mixins/with-namespace-resetting'; const { Controller, computed, inject } = Ember; -export default Controller.extend({ +export default Controller.extend(WithNamespaceResetting, { jobController: inject.controller('jobs.job'), job: computed.alias('model'), diff --git a/ui/app/controllers/jobs/job/index.js b/ui/app/controllers/jobs/job/index.js index 6c5a33282..6e840df3e 100644 --- a/ui/app/controllers/jobs/job/index.js +++ b/ui/app/controllers/jobs/job/index.js @@ -1,9 +1,10 @@ import Ember from 'ember'; import Sortable from 'nomad-ui/mixins/sortable'; +import WithNamespaceResetting from 'nomad-ui/mixins/with-namespace-resetting'; const { Controller, computed, inject } = Ember; -export default Controller.extend(Sortable, { +export default Controller.extend(Sortable, WithNamespaceResetting, { system: inject.service(), jobController: inject.controller('jobs.job'), diff --git a/ui/app/controllers/jobs/job/task-group.js b/ui/app/controllers/jobs/job/task-group.js index 7e24ec8b5..4fc628201 100644 --- a/ui/app/controllers/jobs/job/task-group.js +++ b/ui/app/controllers/jobs/job/task-group.js @@ -1,10 +1,11 @@ import Ember from 'ember'; import Sortable from 'nomad-ui/mixins/sortable'; import Searchable from 'nomad-ui/mixins/searchable'; +import WithNamespaceResetting from 'nomad-ui/mixins/with-namespace-resetting'; const { Controller, computed, inject } = Ember; -export default Controller.extend(Sortable, Searchable, { +export default Controller.extend(Sortable, Searchable, WithNamespaceResetting, { jobController: inject.controller('jobs.job'), queryParams: { diff --git a/ui/app/controllers/jobs/job/versions.js b/ui/app/controllers/jobs/job/versions.js index 420495630..3b2420213 100644 --- a/ui/app/controllers/jobs/job/versions.js +++ b/ui/app/controllers/jobs/job/versions.js @@ -1,8 +1,9 @@ import Ember from 'ember'; +import WithNamespaceResetting from 'nomad-ui/mixins/with-namespace-resetting'; const { Controller, computed, inject } = Ember; -export default Controller.extend({ +export default Controller.extend(WithNamespaceResetting, { jobController: inject.controller('jobs.job'), job: computed.alias('model'), diff --git a/ui/app/mixins/with-namespace-resetting.js b/ui/app/mixins/with-namespace-resetting.js new file mode 100644 index 000000000..c40407568 --- /dev/null +++ b/ui/app/mixins/with-namespace-resetting.js @@ -0,0 +1,19 @@ +import Ember from 'ember'; + +const { Mixin, inject } = Ember; + +export default Mixin.create({ + system: inject.service(), + jobsController: inject.controller('jobs'), + + actions: { + gotoJobs(namespace) { + // Since the setupController hook doesn't fire when transitioning up the + // route hierarchy, the two sides of the namespace bindings need to be manipulated + // in order for the jobs route model to reload. + this.set('system.activeNamespace', this.get('jobsController.jobNamespace')); + this.set('jobsController.jobNamespace', namespace.get('id')); + this.transitionToRoute('jobs'); + }, + }, +}); diff --git a/ui/app/templates/jobs/job/definition.hbs b/ui/app/templates/jobs/job/definition.hbs index f566613c6..f984fcbe7 100644 --- a/ui/app/templates/jobs/job/definition.hbs +++ b/ui/app/templates/jobs/job/definition.hbs @@ -3,7 +3,7 @@ {{#link-to params=breadcrumb.args class="breadcrumb"}}{{breadcrumb.label}}{{/link-to}} {{/each}} {{/global-header}} -{{#gutter-menu class="page-body"}} +{{#gutter-menu class="page-body" onNamespaceChange=(action "gotoJobs")}} {{partial "jobs/job/subnav"}}
diff --git a/ui/app/templates/jobs/job/deployments.hbs b/ui/app/templates/jobs/job/deployments.hbs index b08222656..35afe908c 100644 --- a/ui/app/templates/jobs/job/deployments.hbs +++ b/ui/app/templates/jobs/job/deployments.hbs @@ -3,7 +3,7 @@ {{#link-to params=breadcrumb.args class="breadcrumb"}}{{breadcrumb.label}}{{/link-to}} {{/each}} {{/global-header}} -{{#gutter-menu class="page-body"}} +{{#gutter-menu class="page-body" onNamespaceChange=(action "gotoJobs")}} {{partial "jobs/job/subnav"}}
{{job-deployments-stream deployments=deployments}} diff --git a/ui/app/templates/jobs/job/index.hbs b/ui/app/templates/jobs/job/index.hbs index d5526bba4..f740bca74 100644 --- a/ui/app/templates/jobs/job/index.hbs +++ b/ui/app/templates/jobs/job/index.hbs @@ -3,7 +3,7 @@ {{#link-to params=breadcrumb.args class="breadcrumb"}}{{breadcrumb.label}}{{/link-to}} {{/each}} {{/global-header}} -{{#gutter-menu class="page-body"}} +{{#gutter-menu class="page-body" onNamespaceChange=(action "gotoJobs")}} {{partial "jobs/job/subnav"}}

diff --git a/ui/app/templates/jobs/job/task-group.hbs b/ui/app/templates/jobs/job/task-group.hbs index 6ba9baae4..51c9dc48a 100644 --- a/ui/app/templates/jobs/job/task-group.hbs +++ b/ui/app/templates/jobs/job/task-group.hbs @@ -10,7 +10,7 @@ {{/link-to}} {{/each}} {{/global-header}} -{{#gutter-menu class="page-body"}} +{{#gutter-menu class="page-body" onNamespaceChange=(action "gotoJobs")}}
  • {{#link-to "jobs.job.task-group" model.job model activeClass="is-active"}}Overview{{/link-to}}
  • diff --git a/ui/app/templates/jobs/job/versions.hbs b/ui/app/templates/jobs/job/versions.hbs index 2f3f05d5f..fac0992db 100644 --- a/ui/app/templates/jobs/job/versions.hbs +++ b/ui/app/templates/jobs/job/versions.hbs @@ -3,7 +3,7 @@ {{#link-to params=breadcrumb.args class="breadcrumb"}}{{breadcrumb.label}}{{/link-to}} {{/each}} {{/global-header}} -{{#gutter-menu class="page-body"}} +{{#gutter-menu class="page-body" onNamespaceChange=(action "gotoJobs")}} {{partial "jobs/job/subnav"}}
    {{job-versions-stream versions=versions verbose=true}} diff --git a/ui/mirage/factories/job.js b/ui/mirage/factories/job.js index 908de8311..eee780e47 100644 --- a/ui/mirage/factories/job.js +++ b/ui/mirage/factories/job.js @@ -58,6 +58,10 @@ export default Factory.extend({ namespace, namespaceId: namespace, }); + } else { + job.update({ + namespace: job.namespaceId, + }); } const jobSummary = server.create('job-summary', { diff --git a/ui/tests/acceptance/job-detail-test.js b/ui/tests/acceptance/job-detail-test.js index 538111372..59d200cd3 100644 --- a/ui/tests/acceptance/job-detail-test.js +++ b/ui/tests/acceptance/job-detail-test.js @@ -301,9 +301,8 @@ moduleForAcceptance('Acceptance | job detail (with namespaces)', { beforeEach() { server.createList('namespace', 2); server.create('node'); - server.create('job'); - job = server.db.jobs[0]; - visit(`/jobs/${job.id}`); + job = server.create('job', { namespaceId: server.db.namespaces[1].name }); + server.createList('job', 3, { namespaceId: server.db.namespaces[0].name }); }, }); @@ -311,7 +310,6 @@ test('when there are namespaces, the job detail page states the namespace for th assert ) { const namespace = server.db.namespaces.find(job.namespaceId); - visit(`/jobs/${job.id}?namespace=${namespace.name}`); andThen(() => { @@ -321,3 +319,36 @@ test('when there are namespaces, the job detail page states the namespace for th ); }); }); + +test('when switching namespaces, the app redirects to /jobs with the new namespace', function( + assert +) { + const namespace = server.db.namespaces.find(job.namespaceId); + const otherNamespace = server.db.namespaces.toArray().find(ns => ns !== namespace).name; + const label = otherNamespace === 'default' ? 'Default Namespace' : otherNamespace; + + visit(`/jobs/${job.id}?namespace=${namespace.name}`); + + andThen(() => { + selectChoose('.namespace-switcher', label); + }); + + andThen(() => { + assert.equal(currentURL().split('?')[0], '/jobs', 'Navigated to /jobs'); + const jobs = server.db.jobs + .where({ namespace: otherNamespace }) + .sortBy('modifyIndex') + .reverse(); + assert.equal(findAll('.job-row').length, jobs.length, 'Shows the right number of jobs'); + jobs.forEach((job, index) => { + assert.equal( + $(findAll('.job-row')[index]) + .find('td:eq(0)') + .text() + .trim(), + job.name, + `Job ${index} is right` + ); + }); + }); +});