From 51a9e00374a26dcf0801ccd5482217f448dbfb04 Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Thu, 27 May 2021 13:35:30 -0500 Subject: [PATCH 1/5] Add namespace to job search results --- ui/app/components/global-search/control.js | 2 +- ui/tests/acceptance/search-test.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/app/components/global-search/control.js b/ui/app/components/global-search/control.js index 84bc9a2bfff3..248fd5fb67f6 100644 --- a/ui/app/components/global-search/control.js +++ b/ui/app/components/global-search/control.js @@ -61,7 +61,7 @@ export default class GlobalSearchControl extends Component { type: 'job', id, namespace, - label: name, + label: `${name} @ ${namespace}`, })); const nodeResults = allNodeResults.slice(0, MAXIMUM_RESULTS).map(({ ID: name, Scope: [ id ]}) => ({ diff --git a/ui/tests/acceptance/search-test.js b/ui/tests/acceptance/search-test.js index 8045071ea03e..675d287f3a6f 100644 --- a/ui/tests/acceptance/search-test.js +++ b/ui/tests/acceptance/search-test.js @@ -35,8 +35,8 @@ module('Acceptance | search', function(hooks) { search.groups[0].as(jobs => { assert.equal(jobs.name, 'Jobs (2)'); assert.equal(jobs.options.length, 2); - assert.equal(jobs.options[0].text, 'vwxyz'); - assert.equal(jobs.options[1].text, 'xyz job'); + assert.equal(jobs.options[0].text, 'vwxyz @ default'); + assert.equal(jobs.options[1].text, 'xyz job @ default'); }); search.groups[1].as(clients => { From c075ee5735e7d184ace84c8b9ca253f312fd2dd9 Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Thu, 27 May 2021 13:45:34 -0500 Subject: [PATCH 2/5] Change fuzzy search to search all namespaces Thanks to @apollo13 for reporting this in #10101. --- ui/app/components/global-search/control.js | 1 + ui/tests/acceptance/search-test.js | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/ui/app/components/global-search/control.js b/ui/app/components/global-search/control.js index 248fd5fb67f6..ea291d978ae7 100644 --- a/ui/app/components/global-search/control.js +++ b/ui/app/components/global-search/control.js @@ -46,6 +46,7 @@ export default class GlobalSearchControl extends Component { body: JSON.stringify({ Text: string, Context: 'all', + Namespace: '*', }), }); diff --git a/ui/tests/acceptance/search-test.js b/ui/tests/acceptance/search-test.js index 675d287f3a6f..917ca17bd034 100644 --- a/ui/tests/acceptance/search-test.js +++ b/ui/tests/acceptance/search-test.js @@ -83,11 +83,21 @@ module('Acceptance | search', function(hooks) { await Layout.navbar.search.groups[4].options[0].click(); assert.equal(currentURL(), '/csi/plugins/xyz-plugin'); - const featureDetectionQueries = server.pretender.handledRequests - .filterBy('url', '/v1/search/fuzzy') + const fuzzySearchQueries = server.pretender.handledRequests + .filterBy('url', '/v1/search/fuzzy'); + + const featureDetectionQueries = fuzzySearchQueries .filter(request => request.requestBody.includes('feature-detection-query')); - assert.equal(featureDetectionQueries.length, 1, 'expect the feature detection query to only run once'); + assert.ok(featureDetectionQueries.length, 1, 'expect the feature detection query to only run once'); + + const realFuzzySearchQuery = fuzzySearchQueries[1]; + + assert.deepEqual(JSON.parse(realFuzzySearchQuery.requestBody), { + 'Context': 'all', + 'Namespace': '*', + 'Text': 'xy' + }); }); test('search does not perform a request when only one character has been entered', async function(assert) { From 6f999522c70233a31a49a0232df175f1f5ad8ac4 Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Thu, 27 May 2021 13:56:07 -0500 Subject: [PATCH 3/5] Add namespace to allocation search result label MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Is “@ namespace” sensible? 🧐 --- ui/app/components/global-search/control.js | 4 ++-- ui/tests/acceptance/search-test.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/app/components/global-search/control.js b/ui/app/components/global-search/control.js index ea291d978ae7..06ef78cdf648 100644 --- a/ui/app/components/global-search/control.js +++ b/ui/app/components/global-search/control.js @@ -71,10 +71,10 @@ export default class GlobalSearchControl extends Component { label: name, })); - const allocationResults = allAllocationResults.slice(0, MAXIMUM_RESULTS).map(({ ID: name, Scope: [ , id ]}) => ({ + const allocationResults = allAllocationResults.slice(0, MAXIMUM_RESULTS).map(({ ID: name, Scope: [ namespace, id ]}) => ({ type: 'allocation', id, - label: name, + label: `${name} @ ${namespace}`, })); const taskGroupResults = allTaskGroupResults.slice(0, MAXIMUM_RESULTS).map(({ ID: id, Scope: [ namespace, jobId ]}) => ({ diff --git a/ui/tests/acceptance/search-test.js b/ui/tests/acceptance/search-test.js index 917ca17bd034..57be66529d2f 100644 --- a/ui/tests/acceptance/search-test.js +++ b/ui/tests/acceptance/search-test.js @@ -70,7 +70,7 @@ module('Acceptance | search', function(hooks) { assert.equal(currentURL(), `/clients/${otherNode.id}`); await selectSearch(Layout.navbar.search.scope, firstAllocation.name); - assert.equal(Layout.navbar.search.groups[2].options[0].text, firstAllocation.name); + assert.equal(Layout.navbar.search.groups[2].options[0].text, `${firstAllocation.name} @ ${firstAllocation.namespace}`); await Layout.navbar.search.groups[2].options[0].click(); assert.equal(currentURL(), `/allocations/${firstAllocation.id}`); From 2649d19668ddffc0e51cebe299f6d00c4599dd41 Mon Sep 17 00:00:00 2001 From: Jai Bhagat Date: Mon, 26 Jul 2021 17:23:21 -0400 Subject: [PATCH 4/5] edit fuzzy search callback logic Namespaces are set-up in Nomad to be an object that has an id property. However, namespaces actually don't have that shape. Our search was expecting a namespace object, but we actually don't have a namespace assigned to jobs in our config and namespace is set to null. Normally, these namespaces would be set to default, but that would require us to refactor our Mirage config if we wanted to assert that namespaces are 'default' and not null. So this is a bandaid solution. --- ui/app/components/global-search/control.js | 83 ++++++++++++++-------- ui/mirage/config.js | 4 +- ui/tests/acceptance/search-test.js | 50 +++++++++---- 3 files changed, 91 insertions(+), 46 deletions(-) diff --git a/ui/app/components/global-search/control.js b/ui/app/components/global-search/control.js index 5a100ded4d37..653617cdd162 100644 --- a/ui/app/components/global-search/control.js +++ b/ui/app/components/global-search/control.js @@ -58,32 +58,40 @@ export default class GlobalSearchControl extends Component { const allTaskGroupResults = results.Matches.groups || []; const allCSIPluginResults = results.Matches.plugins || []; - const jobResults = allJobResults.slice(0, MAXIMUM_RESULTS).map(({ ID: name, Scope: [ namespace, id ]}) => ({ - type: 'job', - id, - namespace, - label: `${name} @ ${namespace}`, - })); - - const nodeResults = allNodeResults.slice(0, MAXIMUM_RESULTS).map(({ ID: name, Scope: [ id ]}) => ({ - type: 'node', - id, - label: name, - })); - - const allocationResults = allAllocationResults.slice(0, MAXIMUM_RESULTS).map(({ ID: name, Scope: [ namespace, id ]}) => ({ - type: 'allocation', - id, - label: `${name} @ ${namespace}`, - })); - - const taskGroupResults = allTaskGroupResults.slice(0, MAXIMUM_RESULTS).map(({ ID: id, Scope: [ namespace, jobId ]}) => ({ - type: 'task-group', - id, - namespace, - jobId, - label: id, - })); + const jobResults = allJobResults + .slice(0, MAXIMUM_RESULTS) + .map(({ ID: name, Scope: [namespace, id] }) => ({ + type: 'job', + id, + namespace, + label: `${name} > ${namespace}`, + })); + + const nodeResults = allNodeResults + .slice(0, MAXIMUM_RESULTS) + .map(({ ID: name, Scope: [id] }) => ({ + type: 'node', + id, + label: name, + })); + + const allocationResults = allAllocationResults + .slice(0, MAXIMUM_RESULTS) + .map(({ ID: name, Scope: [namespace, id] }) => ({ + type: 'allocation', + id, + label: `${name} > ${namespace}`, + })); + + const taskGroupResults = allTaskGroupResults + .slice(0, MAXIMUM_RESULTS) + .map(({ ID: id, Scope: [namespace, jobId] }) => ({ + type: 'task-group', + id, + namespace, + jobId, + label: id, + })); const csiPluginResults = allCSIPluginResults.slice(0, MAXIMUM_RESULTS).map(({ ID: id }) => ({ type: 'plugin', @@ -109,17 +117,32 @@ export default class GlobalSearchControl extends Component { options: nodeResults, }, { - groupName: resultsGroupLabel('Allocations', allocationResults, allAllocationResults, allocationsTruncated), + groupName: resultsGroupLabel( + 'Allocations', + allocationResults, + allAllocationResults, + allocationsTruncated + ), options: allocationResults, }, { - groupName: resultsGroupLabel('Task Groups', taskGroupResults, allTaskGroupResults, taskGroupsTruncated), + groupName: resultsGroupLabel( + 'Task Groups', + taskGroupResults, + allTaskGroupResults, + taskGroupsTruncated + ), options: taskGroupResults, }, { - groupName: resultsGroupLabel('CSI Plugins', csiPluginResults, allCSIPluginResults, csiPluginsTruncated), + groupName: resultsGroupLabel( + 'CSI Plugins', + csiPluginResults, + allCSIPluginResults, + csiPluginsTruncated + ), options: csiPluginResults, - } + }, ]; }) search; diff --git a/ui/mirage/config.js b/ui/mirage/config.js index 3e26e3bf2b9f..e1ce1847c154 100644 --- a/ui/mirage/config.js +++ b/ui/mirage/config.js @@ -612,7 +612,7 @@ export default function() { const transformedAllocs = matchedAllocs.models.map(alloc => ({ ID: alloc.name, - Scope: [(alloc.namespace || {}).id, alloc.id], + Scope: [alloc.namespace || 'default', alloc.id], })); const transformedGroups = matchedGroups.models.map(group => ({ @@ -622,7 +622,7 @@ export default function() { const transformedJobs = matchedJobs.models.map(job => ({ ID: job.name, - Scope: [job.namespace, job.id], + Scope: [job.namespace || 'default', job.id], })); const transformedNodes = matchedNodes.models.map(node => ({ diff --git a/ui/tests/acceptance/search-test.js b/ui/tests/acceptance/search-test.js index c1380f218d71..45238e95470d 100644 --- a/ui/tests/acceptance/search-test.js +++ b/ui/tests/acceptance/search-test.js @@ -16,8 +16,19 @@ module('Acceptance | search', function(hooks) { server.create('node', { name: 'xyz' }); const otherNode = server.create('node', { name: 'ghi' }); - server.create('job', { id: 'vwxyz', namespaceId: 'default', groupsCount: 1, groupTaskCount: 1 }); - server.create('job', { id: 'xyz', name: 'xyz job', namespaceId: 'default', groupsCount: 1, groupTaskCount: 1 }); + server.create('job', { + id: 'vwxyz', + namespaceId: 'default', + groupsCount: 1, + groupTaskCount: 1, + }); + server.create('job', { + id: 'xyz', + name: 'xyz job', + namespaceId: 'default', + groupsCount: 1, + groupTaskCount: 1, + }); server.create('job', { id: 'abc', namespaceId: 'default', groupsCount: 1, groupTaskCount: 1 }); const firstAllocation = server.schema.allocations.all().models[0]; @@ -35,8 +46,8 @@ module('Acceptance | search', function(hooks) { search.groups[0].as(jobs => { assert.equal(jobs.name, 'Jobs (2)'); assert.equal(jobs.options.length, 2); - assert.equal(jobs.options[0].text, 'vwxyz @ default'); - assert.equal(jobs.options[1].text, 'xyz job @ default'); + assert.equal(jobs.options[0].text, 'vwxyz > default'); + assert.equal(jobs.options[1].text, 'xyz job > default'); }); search.groups[1].as(clients => { @@ -70,7 +81,10 @@ module('Acceptance | search', function(hooks) { assert.equal(currentURL(), `/clients/${otherNode.id}`); await selectSearch(Layout.navbar.search.scope, firstAllocation.name); - assert.equal(Layout.navbar.search.groups[2].options[0].text, `${firstAllocation.name} @ ${firstAllocation.namespace}`); + assert.equal( + Layout.navbar.search.groups[2].options[0].text, + `${firstAllocation.name} > ${firstAllocation.namespace}` + ); await Layout.navbar.search.groups[2].options[0].click(); assert.equal(currentURL(), `/allocations/${firstAllocation.id}`); @@ -83,20 +97,24 @@ module('Acceptance | search', function(hooks) { await Layout.navbar.search.groups[4].options[0].click(); assert.equal(currentURL(), '/csi/plugins/xyz-plugin'); - const fuzzySearchQueries = server.pretender.handledRequests - .filterBy('url', '/v1/search/fuzzy'); + const fuzzySearchQueries = server.pretender.handledRequests.filterBy('url', '/v1/search/fuzzy'); - const featureDetectionQueries = fuzzySearchQueries - .filter(request => request.requestBody.includes('feature-detection-query')); + const featureDetectionQueries = fuzzySearchQueries.filter(request => + request.requestBody.includes('feature-detection-query') + ); - assert.ok(featureDetectionQueries.length, 1, 'expect the feature detection query to only run once'); + assert.ok( + featureDetectionQueries.length, + 1, + 'expect the feature detection query to only run once' + ); const realFuzzySearchQuery = fuzzySearchQueries[1]; assert.deepEqual(JSON.parse(realFuzzySearchQuery.requestBody), { - 'Context': 'all', - 'Namespace': '*', - 'Text': 'xy' + Context: 'all', + Namespace: '*', + Text: 'xy', }); }); @@ -106,7 +124,11 @@ module('Acceptance | search', function(hooks) { await selectSearch(Layout.navbar.search.scope, 'q'); assert.ok(Layout.navbar.search.noOptionsShown); - assert.equal(server.pretender.handledRequests.filterBy('url', '/v1/search/fuzzy').length, 1, 'expect the feature detection query'); + assert.equal( + server.pretender.handledRequests.filterBy('url', '/v1/search/fuzzy').length, + 1, + 'expect the feature detection query' + ); }); test('when fuzzy search is disabled on the server, the search control is hidden', async function(assert) { From 00df085e7109c4f3864566b5e361f3d1ff8c6226 Mon Sep 17 00:00:00 2001 From: Jai Bhagat Date: Mon, 9 Aug 2021 17:50:49 -0400 Subject: [PATCH 5/5] edit hierarchy to lead with namespace before job --- .changelog/10666.txt | 3 +++ ui/app/components/global-search/control.js | 6 +++--- ui/tests/acceptance/search-test.js | 11 +++++++---- 3 files changed, 13 insertions(+), 7 deletions(-) create mode 100644 .changelog/10666.txt diff --git a/.changelog/10666.txt b/.changelog/10666.txt new file mode 100644 index 000000000000..2b2d74d5236f --- /dev/null +++ b/.changelog/10666.txt @@ -0,0 +1,3 @@ +```release-note:bug +ui: Add ability to search across all namespaces +``` diff --git a/ui/app/components/global-search/control.js b/ui/app/components/global-search/control.js index 653617cdd162..8f3967cca356 100644 --- a/ui/app/components/global-search/control.js +++ b/ui/app/components/global-search/control.js @@ -64,7 +64,7 @@ export default class GlobalSearchControl extends Component { type: 'job', id, namespace, - label: `${name} > ${namespace}`, + label: `${namespace} > ${name}`, })); const nodeResults = allNodeResults @@ -80,7 +80,7 @@ export default class GlobalSearchControl extends Component { .map(({ ID: name, Scope: [namespace, id] }) => ({ type: 'allocation', id, - label: `${name} > ${namespace}`, + label: `${namespace} > ${name}`, })); const taskGroupResults = allTaskGroupResults @@ -90,7 +90,7 @@ export default class GlobalSearchControl extends Component { id, namespace, jobId, - label: id, + label: `${namespace} > ${jobId} > ${id}`, })); const csiPluginResults = allCSIPluginResults.slice(0, MAXIMUM_RESULTS).map(({ ID: id }) => ({ diff --git a/ui/tests/acceptance/search-test.js b/ui/tests/acceptance/search-test.js index 45238e95470d..635aa0ee9b5b 100644 --- a/ui/tests/acceptance/search-test.js +++ b/ui/tests/acceptance/search-test.js @@ -46,8 +46,8 @@ module('Acceptance | search', function(hooks) { search.groups[0].as(jobs => { assert.equal(jobs.name, 'Jobs (2)'); assert.equal(jobs.options.length, 2); - assert.equal(jobs.options[0].text, 'vwxyz > default'); - assert.equal(jobs.options[1].text, 'xyz job > default'); + assert.equal(jobs.options[0].text, 'default > vwxyz'); + assert.equal(jobs.options[1].text, 'default > xyz job'); }); search.groups[1].as(clients => { @@ -83,13 +83,16 @@ module('Acceptance | search', function(hooks) { await selectSearch(Layout.navbar.search.scope, firstAllocation.name); assert.equal( Layout.navbar.search.groups[2].options[0].text, - `${firstAllocation.name} > ${firstAllocation.namespace}` + `${firstAllocation.namespace} > ${firstAllocation.name}` ); await Layout.navbar.search.groups[2].options[0].click(); assert.equal(currentURL(), `/allocations/${firstAllocation.id}`); await selectSearch(Layout.navbar.search.scope, firstTaskGroup.name); - assert.equal(Layout.navbar.search.groups[3].options[0].text, firstTaskGroup.name); + assert.equal( + Layout.navbar.search.groups[3].options[0].text, + `default > vwxyz > ${firstTaskGroup.name}` + ); await Layout.navbar.search.groups[3].options[0].click(); assert.equal(currentURL(), `/jobs/vwxyz/${firstTaskGroup.name}`);