diff --git a/ui/src/components/SingleInputComponent/SingleInputComponent.test.tsx b/ui/src/components/SingleInputComponent/SingleInputComponent.test.tsx index eec9726e6..f4c2b624d 100644 --- a/ui/src/components/SingleInputComponent/SingleInputComponent.test.tsx +++ b/ui/src/components/SingleInputComponent/SingleInputComponent.test.tsx @@ -49,8 +49,8 @@ const renderFeature = (additionalProps?: Partial) => const mockedEntries = [ { name: 'dataApiTest1', content: { testLabel: 'firstLabel', testValue: 'firstValue' } }, { name: 'dataApiTest2', content: { testLabel: 'secondLabel', testValue: 'secondValue' } }, - { name: 'dataApiTest3', content: { testLabel: 'thirdLabel', testValue: 'thirdValue' } }, - { name: 'dataApiTest4', content: { testLabel: 'fourthLabel', testValue: 'fourthValue' } }, + { name: 'true', content: { testLabel: 'thirdLabel', testValue: 'thirdValue' } }, + { name: '0', content: { testLabel: 'fourthLabel', testValue: 'fourthValue' } }, ]; const MOCK_API_URL = '/demo_addon_for_splunk/some_API_endpint_for_select_data'; @@ -167,3 +167,67 @@ it.each([ const { label } = autoCompleteFields[0]; expect(within(inputComponent).getByText(label)).toBeInTheDocument(); }); + +it('should fetch options from API when endpointUrl is provided', async () => { + // server responses with a filtered mockedEntries based on the name parameter + server.use( + http.get(MOCK_API_URL, ({ request }) => { + const url = new URL(request.url); + + const nameParameter = url.searchParams.get('name'); + return HttpResponse.json( + getMockServerResponseForInput( + mockedEntries.filter((entry) => entry.name === nameParameter) + ) + ); + }) + ); + const baseProps = { + ...defaultInputProps, + value: '', + controlOptions: { + createSearchChoice: true, + dependencies: ['name', 'region'], + endpointUrl: MOCK_API_URL, + labelField: 'testLabel', + valueField: 'testValue', + }, + }; + const { rerender } = render(); + + await userEvent.click(screen.getByRole('combobox')); + await screen.findByRole('menuitem', { name: 'No matches' }); + + // undefined value must be omitted + const firstEntry = mockedEntries[0]; + rerender( + + ); + await userEvent.click(screen.getByRole('combobox')); + await screen.findByRole('option', { name: firstEntry.content.testLabel }); + + const secondEntry = mockedEntries[1]; + rerender( + + ); + await userEvent.click(screen.getByRole('combobox')); + await screen.findByRole('option', { name: secondEntry.content.testLabel }); + + const thirdEntry = mockedEntries[2]; + rerender( + + ); + await userEvent.click(screen.getByRole('combobox')); + await screen.findByRole('option', { name: thirdEntry.content.testLabel }); + + const fourthEntry = mockedEntries[3]; + rerender(); + await userEvent.click(screen.getByRole('combobox')); + await screen.findByRole('option', { name: fourthEntry.content.testLabel }); +}); diff --git a/ui/src/util/api.ts b/ui/src/util/api.ts index e44a7657e..b0265eaa9 100755 --- a/ui/src/util/api.ts +++ b/ui/src/util/api.ts @@ -5,9 +5,11 @@ import { generateToast, getUnifiedConfigs } from './util'; import { parseErrorMsg } from './messageUtil'; import { ResponseError } from './ResponseError'; +type ParamsRecord = Record; + export interface RequestParams { endpointUrl: string; - params?: Record; + params?: ParamsRecord; signal?: AbortSignal; body?: BodyInit; handleError: boolean; @@ -21,14 +23,14 @@ export function generateEndPointUrl(name: string) { const DEFAULT_PARAMS = { output_mode: 'json' }; -function createUrl(endpointUrl: string, params: Record): URL { +function createUrl(endpointUrl: string, params: ParamsRecord): URL { const url = new URL( createRESTURL(endpointUrl, { app, owner: 'nobody' }), window.location.origin ); - Object.entries({ ...DEFAULT_PARAMS, ...params }).forEach(([key, value]) => - url.searchParams.append(key, value.toString()) - ); + Object.entries({ ...DEFAULT_PARAMS, ...params }) + .filter(([, value]) => value !== undefined && value !== null) + .forEach(([key, value]) => url.searchParams.append(key, value.toString())); return url; }