mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 01:13:23 -04:00
[Global Search] Register custom integrations search provider (#213013)
## Summary This PR creates search provider for custom integrations so they show up in Global Search. Closes: #115778
This commit is contained in:
parent
98a7259ee1
commit
c3c8f7befb
3 changed files with 201 additions and 2 deletions
|
@ -76,7 +76,10 @@ import { CUSTOM_LOGS_INTEGRATION_NAME, INTEGRATIONS_BASE_PATH } from './constant
|
|||
import type { RequestError } from './hooks';
|
||||
import { licenseService, sendGetBulkAssets } from './hooks';
|
||||
import { setHttpClient } from './hooks/use_request';
|
||||
import { createPackageSearchProvider } from './search_provider';
|
||||
import {
|
||||
createCustomIntegrationsSearchProvider,
|
||||
createPackageSearchProvider,
|
||||
} from './search_provider';
|
||||
import { TutorialDirectoryHeaderLink, TutorialModuleNotice } from './components/home_integration';
|
||||
import { createExtensionRegistrationCallback } from './services/ui_extensions';
|
||||
import { ExperimentalFeaturesService } from './services/experimental_features';
|
||||
|
@ -291,6 +294,9 @@ export class FleetPlugin implements Plugin<FleetSetup, FleetStart, FleetSetupDep
|
|||
|
||||
if (deps.globalSearch) {
|
||||
deps.globalSearch.registerResultProvider(createPackageSearchProvider(core));
|
||||
deps.globalSearch.registerResultProvider(
|
||||
createCustomIntegrationsSearchProvider(deps.customIntegrations)
|
||||
);
|
||||
}
|
||||
|
||||
return {};
|
||||
|
|
|
@ -9,8 +9,13 @@ import { TestScheduler } from 'rxjs/testing';
|
|||
import { NEVER } from 'rxjs';
|
||||
|
||||
import { coreMock } from '@kbn/core/public/mocks';
|
||||
import type { CustomIntegrationsSetup } from '@kbn/custom-integrations-plugin/public';
|
||||
|
||||
import { createPackageSearchProvider, toSearchResult } from './search_provider';
|
||||
import {
|
||||
createCustomIntegrationsSearchProvider,
|
||||
createPackageSearchProvider,
|
||||
toSearchResult,
|
||||
} from './search_provider';
|
||||
import type { GetPackagesResponse } from './types';
|
||||
|
||||
jest.mock('./hooks/use_request/epm', () => {
|
||||
|
@ -667,3 +672,145 @@ describe('Package search provider', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Custom Integrations search provider', () => {
|
||||
let customIntegrationsMock: CustomIntegrationsSetup;
|
||||
const customIntegrationsMockData = [
|
||||
{
|
||||
id: 'custom1',
|
||||
title: 'Custom Integration 1',
|
||||
uiInternalPath: '/app/custom1',
|
||||
icons: [{ src: 'icon1.svg' }],
|
||||
},
|
||||
{
|
||||
id: 'custom2',
|
||||
title: 'Custom Integration 2',
|
||||
uiInternalPath: '/app/custom2',
|
||||
icons: [{ src: 'icon2.svg' }],
|
||||
},
|
||||
];
|
||||
|
||||
beforeEach(() => {
|
||||
customIntegrationsMock = {
|
||||
getReplacementCustomIntegrations: jest.fn(),
|
||||
} as unknown as CustomIntegrationsSetup;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('#find', () => {
|
||||
test('returns formatted results', () => {
|
||||
getTestScheduler().run(({ expectObservable, hot }) => {
|
||||
(customIntegrationsMock.getReplacementCustomIntegrations as jest.Mock).mockReturnValue(
|
||||
hot('--a|', { a: customIntegrationsMockData })
|
||||
);
|
||||
|
||||
const customIntegrationsSearchProvider =
|
||||
createCustomIntegrationsSearchProvider(customIntegrationsMock);
|
||||
|
||||
expectObservable(
|
||||
customIntegrationsSearchProvider.find(
|
||||
{ term: 'custom' },
|
||||
{ aborted$: NEVER, maxResults: 100, preference: '' }
|
||||
)
|
||||
).toBe('--a|', {
|
||||
a: [
|
||||
{
|
||||
id: 'custom1',
|
||||
score: 80,
|
||||
title: 'Custom Integration 1',
|
||||
type: 'integration',
|
||||
url: '/app/custom1',
|
||||
icon: 'icon1.svg',
|
||||
},
|
||||
{
|
||||
id: 'custom2',
|
||||
score: 80,
|
||||
title: 'Custom Integration 2',
|
||||
type: 'integration',
|
||||
url: '/app/custom2',
|
||||
icon: 'icon2.svg',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
expect(customIntegrationsMock.getReplacementCustomIntegrations).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('returns empty array if no term is provided', () => {
|
||||
getTestScheduler().run(({ expectObservable, hot }) => {
|
||||
(customIntegrationsMock.getReplacementCustomIntegrations as jest.Mock).mockReturnValue(
|
||||
hot('--a|', { a: [] })
|
||||
);
|
||||
|
||||
const customIntegrationsSearchProvider =
|
||||
createCustomIntegrationsSearchProvider(customIntegrationsMock);
|
||||
|
||||
expectObservable(
|
||||
customIntegrationsSearchProvider.find(
|
||||
{ term: '' },
|
||||
{ aborted$: NEVER, maxResults: 100, preference: '' }
|
||||
)
|
||||
).toBe('(a|)', {
|
||||
a: [],
|
||||
});
|
||||
});
|
||||
|
||||
expect(customIntegrationsMock.getReplacementCustomIntegrations).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
test('completes without returning results if aborted', () => {
|
||||
getTestScheduler().run(({ expectObservable, hot }) => {
|
||||
(customIntegrationsMock.getReplacementCustomIntegrations as jest.Mock).mockReturnValue(
|
||||
hot('--a|', { a: customIntegrationsMockData })
|
||||
);
|
||||
const aborted$ = hot('-a', { a: undefined });
|
||||
const customIntegrationsSearchProvider =
|
||||
createCustomIntegrationsSearchProvider(customIntegrationsMock);
|
||||
|
||||
expectObservable(
|
||||
customIntegrationsSearchProvider.find(
|
||||
{ term: 'custom' },
|
||||
{ aborted$, maxResults: 100, preference: '' }
|
||||
)
|
||||
).toBe('-|');
|
||||
});
|
||||
|
||||
expect(customIntegrationsMock.getReplacementCustomIntegrations).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('respects maximum results', () => {
|
||||
getTestScheduler().run(({ hot, expectObservable }) => {
|
||||
(customIntegrationsMock.getReplacementCustomIntegrations as jest.Mock).mockReturnValue(
|
||||
hot('--a|', { a: customIntegrationsMockData })
|
||||
);
|
||||
|
||||
const customIntegrationsSearchProvider =
|
||||
createCustomIntegrationsSearchProvider(customIntegrationsMock);
|
||||
|
||||
expectObservable(
|
||||
customIntegrationsSearchProvider.find(
|
||||
{ term: 'custom' },
|
||||
{ aborted$: NEVER, maxResults: 1, preference: '' }
|
||||
)
|
||||
).toBe('--a|', {
|
||||
a: [
|
||||
{
|
||||
id: 'custom1',
|
||||
score: 80,
|
||||
title: 'Custom Integration 1',
|
||||
type: 'integration',
|
||||
url: '/app/custom1',
|
||||
icon: 'icon1.svg',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
expect(customIntegrationsMock.getReplacementCustomIntegrations).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -15,6 +15,10 @@ import type {
|
|||
GlobalSearchProviderResult,
|
||||
} from '@kbn/global-search-plugin/public';
|
||||
|
||||
import type { CustomIntegrationsSetup } from '@kbn/custom-integrations-plugin/public';
|
||||
|
||||
import type { CustomIntegration } from '@kbn/custom-integrations-plugin/common';
|
||||
|
||||
import { INTEGRATIONS_PLUGIN_ID } from '../common';
|
||||
import { filterPolicyTemplatesTiles } from '../common/services';
|
||||
|
||||
|
@ -147,3 +151,45 @@ export const createPackageSearchProvider = (core: CoreSetup): GlobalSearchResult
|
|||
},
|
||||
};
|
||||
};
|
||||
|
||||
const toCustomItegrationSearchResult = (customIntegration: CustomIntegration) => ({
|
||||
id: customIntegration.id,
|
||||
type: packageType,
|
||||
title: customIntegration.title,
|
||||
score: 80,
|
||||
url: customIntegration.uiInternalPath,
|
||||
icon: customIntegration.icons.find(({ src }) => Boolean(src))?.src,
|
||||
});
|
||||
|
||||
export const createCustomIntegrationsSearchProvider = (
|
||||
customIntegrations: CustomIntegrationsSetup
|
||||
): GlobalSearchResultProvider => {
|
||||
return {
|
||||
id: 'customIntegrations',
|
||||
getSearchableTypes: () => [packageType],
|
||||
find: ({ term, types }, { maxResults, aborted$ }) => {
|
||||
if (types?.includes(packageType) === false) {
|
||||
return of([]);
|
||||
}
|
||||
|
||||
if (!term) {
|
||||
return of([]);
|
||||
}
|
||||
|
||||
const customIntegrations$ = from(customIntegrations.getReplacementCustomIntegrations()).pipe(
|
||||
map((integrations) => integrations),
|
||||
shareReplay(1)
|
||||
);
|
||||
|
||||
return customIntegrations$.pipe(
|
||||
takeUntil(aborted$),
|
||||
map((customIntegrationsData) =>
|
||||
customIntegrationsData
|
||||
.map(toCustomItegrationSearchResult)
|
||||
.filter((res) => term && res.title.toLowerCase().includes(term.toLowerCase()))
|
||||
.slice(0, maxResults)
|
||||
)
|
||||
);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue