diff --git a/ee/app/assets/javascripts/tracing/components/operation_search_token.vue b/ee/app/assets/javascripts/tracing/components/operation_search_token.vue index 15904c556fc522215057e241045033d3d3acc3ce..18eb2becf53b51a5c925450d9c697ccfd02575f4 100644 --- a/ee/app/assets/javascripts/tracing/components/operation_search_token.vue +++ b/ee/app/assets/javascripts/tracing/components/operation_search_token.vue @@ -4,6 +4,7 @@ import isEmpty from 'lodash/isEmpty'; import { createAlert } from '~/alert'; import { s__ } from '~/locale'; import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue'; +import { SERVICE_NAME_FILTER_TOKEN_TYPE } from '../filters'; export default { components: { @@ -24,6 +25,10 @@ export default { type: Object, required: true, }, + currentValue: { + type: Array, + required: true, + }, }, data() { return { @@ -34,10 +39,16 @@ export default { }, computed: { showNoServiceFooter() { - return this.config.loadSuggestionsForServices.length === 0; + return this.servicesForSuggestions.length === 0; }, shouldLoadSuggestions() { - return this.config.loadSuggestionsForServices.length > 0; + return this.servicesForSuggestions.length > 0; + }, + servicesForSuggestions() { + const serviceFilters = this.currentValue.filter( + ({ type, value }) => type === SERVICE_NAME_FILTER_TOKEN_TYPE && value.operator === '=', + ); + return serviceFilters.map(({ value }) => value.data); }, }, methods: { @@ -58,9 +69,7 @@ export default { async fetchOperations() { this.loading = true; try { - const fetchAll = this.config.loadSuggestionsForServices.map((s) => - this.config.fetchOperations(s), - ); + const fetchAll = this.servicesForSuggestions.map((s) => this.config.fetchOperations(s)); this.operations = (await Promise.all(fetchAll)).flat(); } catch (e) { createAlert({ diff --git a/ee/app/assets/javascripts/tracing/components/tracing_list_filtered_search.vue b/ee/app/assets/javascripts/tracing/components/tracing_list_filtered_search.vue index dd25649284f0dc018248180b422512b45c538b7d..fb77656dd73f4bb80f3a311ee7972f627fa93da0 100644 --- a/ee/app/assets/javascripts/tracing/components/tracing_list_filtered_search.vue +++ b/ee/app/assets/javascripts/tracing/components/tracing_list_filtered_search.vue @@ -41,11 +41,6 @@ export default { type: String, }, }, - data() { - return { - value: this.initialFilters, - }; - }, computed: { sortOptions() { return [ @@ -68,10 +63,6 @@ export default { ]; }, availableTokens() { - const serviceFilters = this.value.filter( - ({ type, value }) => type === 'service-name' && value.operator === '=', - ); - const loadSuggestionsForServices = serviceFilters.map(({ value }) => value.data); return [ { title: s__('Tracing|Time range'), @@ -105,7 +96,6 @@ export default { type: OPERATION_FILTER_TOKEN_TYPE, token: OperationToken, operators: OPERATORS_IS_NOT, - loadSuggestionsForServices, fetchOperations: this.observabilityClient.fetchOperations, }, { diff --git a/ee/spec/frontend/tracing/components/operation_search_token_spec.js b/ee/spec/frontend/tracing/components/operation_search_token_spec.js index 193f1a94e079c764a34d8e523658a8cc0fb5af39..92855db7e95adefd486b1f1eb3293e7a39947f9a 100644 --- a/ee/spec/frontend/tracing/components/operation_search_token_spec.js +++ b/ee/spec/frontend/tracing/components/operation_search_token_spec.js @@ -24,29 +24,27 @@ describe('OperationServiceToken', () => { .map(({ name }) => ({ name })); const isLoadingSuggestions = () => findBaseToken().props('suggestionsLoading'); + const buildMockServiceFilter = (serviceNames) => + serviceNames.map((n) => ({ type: 'service-name', value: { data: n, operator: '=' } })); + let mockFetchOperations = jest.fn(); - const mockOperations = [{ name: 'o1' }, { name: 'o2' }]; const mountComponent = ({ active = false, - loadSuggestionsForServices = [{ name: 's1' }], + currentValue = buildMockServiceFilter(['s1']), } = {}) => { wrapper = shallowMountExtended(OperationServiceToken, { propsData: { active, config: { fetchOperations: mockFetchOperations, - loadSuggestionsForServices, }, + currentValue, value: { data: '' }, }, }); }; - beforeEach(() => { - mockFetchOperations = jest.fn().mockResolvedValue(mockOperations); - }); - describe('default', () => { beforeEach(() => { mountComponent(); @@ -69,32 +67,35 @@ describe('OperationServiceToken', () => { describe('when active', () => { beforeEach(() => { - mountComponent({ active: true }); + mockFetchOperations.mockImplementation((service) => + Promise.resolve({ name: `op-for-${service}` }), + ); + mountComponent({ + active: true, + currentValue: buildMockServiceFilter(['s1', 's2']), + }); }); - it('fetches the operations suggestions', async () => { + it('fetches the operations suggestions for each service defined in the current filter', async () => { expect(isLoadingSuggestions()).toBe(false); await triggerFetchSuggestions(); - expect(mockFetchOperations).toHaveBeenCalled(); + expect(mockFetchOperations).toHaveBeenCalledTimes(2); + expect(isLoadingSuggestions()).toBe(false); expect(wrapper.findComponent(GlDropdownText).exists()).toBe(false); - expect(findSuggestions()).toEqual(mockOperations); + expect(findSuggestions()).toEqual([{ name: 'op-for-s1' }, { name: 'op-for-s2' }]); }); it('only fetch suggestions once', async () => { await triggerFetchSuggestions(); - await triggerFetchSuggestions(); + mockFetchOperations.mockClear(); - expect(mockFetchOperations).toHaveBeenCalledTimes(1); - }); - - it('filters suggestions if a search term is specified', async () => { - await triggerFetchSuggestions('o1'); + await triggerFetchSuggestions(); - expect(findSuggestions()).toEqual([{ name: 'o1' }]); + expect(mockFetchOperations).not.toHaveBeenCalled(); }); it('sets the loading status', async () => { @@ -105,40 +106,30 @@ describe('OperationServiceToken', () => { expect(isLoadingSuggestions()).toBe(true); }); - describe('when loadSuggestionsForServices is not empty', () => { - beforeEach(() => { - mockFetchOperations.mockImplementation((service) => - Promise.resolve({ name: `op-for-${service}` }), - ); - mountComponent({ - active: true, - loadSuggestionsForServices: ['s1', 's2'], - }); - }); - - it('fetches the operations suggestions for each service defined in loadSuggestionsForServices', async () => { - await triggerFetchSuggestions(); + it('filters suggestions by search term if specified', async () => { + await triggerFetchSuggestions('s1'); - expect(mockFetchOperations).toHaveBeenCalledTimes(2); - expect(findSuggestions()).toEqual([{ name: 'op-for-s1' }, { name: 'op-for-s2' }]); - }); + expect(findSuggestions()).toEqual([{ name: 'op-for-s1' }]); }); - describe('when loadSuggestionsForServices is empty', () => { + describe('when the current filter does not contain service-name filters', () => { beforeEach(() => { mountComponent({ active: true, - loadSuggestionsForServices: [], + currentValue: [ + { type: 'other-type', value: { data: 'other-value', operator: '=' } }, + { type: 'service-name', value: { data: 's1', operator: '!=' } }, + ], }); }); - it('does not fetch suggestions if loadSuggestionsForServices is empty', async () => { + it('does not fetch suggestions', async () => { await triggerFetchSuggestions(); expect(mockFetchOperations).not.toHaveBeenCalled(); }); - it('does shows a dropdown-text if loadSuggestionsForServices is empty', async () => { + it('does show a dropdown-text', async () => { await triggerFetchSuggestions(); expect(wrapper.findComponent(GlDropdownText).exists()).toBe(true); diff --git a/ee/spec/frontend/tracing/components/tracing_list_filtered_search_spec.js b/ee/spec/frontend/tracing/components/tracing_list_filtered_search_spec.js index c2a1437361b86e101012edf4b01d1266bece5aaa..72e253557cd7933dda599c11ac9b2c1c451df60d 100644 --- a/ee/spec/frontend/tracing/components/tracing_list_filtered_search_spec.js +++ b/ee/spec/frontend/tracing/components/tracing_list_filtered_search_spec.js @@ -85,24 +85,5 @@ describe('TracingListFilteredSearch', () => { expect(operationToken.token).toBe(OperationToken); expect(operationToken.fetchOperations).toBe(observabilityClientMock.fetchOperations); }); - - it('sets loadSuggestionsForServices based on the existing service filter', () => { - wrapper = shallowMountExtended(TracingListFilteredSearch, { - propsData: { - initialFilters: [ - { type: 'service-name', value: { operator: '=', data: 'a-service' } }, - { type: 'service-name', value: { operator: '!=', data: 'unsupported-operator' } }, - { type: 'trace-id', value: { operator: '=', data: 'a-trace-id' } }, - ], - observabilityClient: observabilityClientMock, - initialSort: 'created_desc', - }, - }); - - const operationToken = findFilteredSearch() - .props('tokens') - .find((t) => t.type === 'operation'); - expect(operationToken.loadSuggestionsForServices).toStrictEqual(['a-service']); - }); }); });