mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
# Backport This will backport the following commits from `main` to `8.17`: - [[Inventory] Change discover link to use entity definition (#201433)](https://github.com/elastic/kibana/pull/201433) <!--- Backport version: 9.4.3 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"jennypavlova","email":"dzheni.pavlova@elastic.co"},"sourceCommit":{"committedDate":"2024-11-28T11:44:20Z","message":"[Inventory] Change discover link to use entity definition (#201433)\n\nCloses #200595 \r\n\r\n## Summary\r\n\r\nThis PR changes the link to discover: \r\n- Pass a different data view (using the entity definition indices)\r\n- from Open in Discover to Explore in Discover\r\n\r\n## Testing\r\n\r\n- To have the test data I used metricbeat (for the host entity) and\r\nsynthtrace (for the other entities type)\r\n- Open the inventory page and expand an entity type\r\n- Click on the `...` in the actions column\r\n- Click on `Explore in Discover`\r\n\r\nThe results should be filtered by the selected entity and the dataview\r\nshould include the indices from the entity definition:\r\n\r\n\r\n36c08b8b
-f507-445e-a7b5-8eb1176beb90\r\n\r\n\r\nWith service type filter (should not persist)\r\n\r\n\r\nhttps://github.com/user-attachments/assets/ae6ef28a-e55a-4a32-9128-802ebea25607\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>","sha":"afc5e51e296eb499a17ad82de560d20c352cd2a9","branchLabelMapping":{"^v9.0.0$":"main","^v8.18.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","backport:prev-minor","ci:project-deploy-observability","Team:obs-ux-infra_services","backport:version","v8.17.0"],"title":"[Inventory] Change discover link to use entity definition","number":201433,"url":"https://github.com/elastic/kibana/pull/201433","mergeCommit":{"message":"[Inventory] Change discover link to use entity definition (#201433)\n\nCloses #200595 \r\n\r\n## Summary\r\n\r\nThis PR changes the link to discover: \r\n- Pass a different data view (using the entity definition indices)\r\n- from Open in Discover to Explore in Discover\r\n\r\n## Testing\r\n\r\n- To have the test data I used metricbeat (for the host entity) and\r\nsynthtrace (for the other entities type)\r\n- Open the inventory page and expand an entity type\r\n- Click on the `...` in the actions column\r\n- Click on `Explore in Discover`\r\n\r\nThe results should be filtered by the selected entity and the dataview\r\nshould include the indices from the entity definition:\r\n\r\n\r\n36c08b8b
-f507-445e-a7b5-8eb1176beb90\r\n\r\n\r\nWith service type filter (should not persist)\r\n\r\n\r\nhttps://github.com/user-attachments/assets/ae6ef28a-e55a-4a32-9128-802ebea25607\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>","sha":"afc5e51e296eb499a17ad82de560d20c352cd2a9"}},"sourceBranch":"main","suggestedTargetBranches":["8.17"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/201433","number":201433,"mergeCommit":{"message":"[Inventory] Change discover link to use entity definition (#201433)\n\nCloses #200595 \r\n\r\n## Summary\r\n\r\nThis PR changes the link to discover: \r\n- Pass a different data view (using the entity definition indices)\r\n- from Open in Discover to Explore in Discover\r\n\r\n## Testing\r\n\r\n- To have the test data I used metricbeat (for the host entity) and\r\nsynthtrace (for the other entities type)\r\n- Open the inventory page and expand an entity type\r\n- Click on the `...` in the actions column\r\n- Click on `Explore in Discover`\r\n\r\nThe results should be filtered by the selected entity and the dataview\r\nshould include the indices from the entity definition:\r\n\r\n\r\n36c08b8b
-f507-445e-a7b5-8eb1176beb90\r\n\r\n\r\nWith service type filter (should not persist)\r\n\r\n\r\nhttps://github.com/user-attachments/assets/ae6ef28a-e55a-4a32-9128-802ebea25607\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>","sha":"afc5e51e296eb499a17ad82de560d20c352cd2a9"}},{"branch":"8.17","label":"v8.17.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> --------- Co-authored-by: jennypavlova <dzheni.pavlova@elastic.co>
This commit is contained in:
parent
8635c81ad0
commit
dffa1d0518
15 changed files with 205 additions and 134 deletions
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the "Elastic License
|
||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { formatRequest } from './format_request';
|
||||
|
||||
describe('formatRequest', () => {
|
||||
const version = 1;
|
||||
it('should return the correct path if the optional or required param is provided', () => {
|
||||
const pathParams = { param: 'testParam' };
|
||||
const resultOptionalEnd = formatRequest(`GET /api/endpoint/{param?} ${version}`, pathParams);
|
||||
expect(resultOptionalEnd.pathname).toBe('/api/endpoint/testParam');
|
||||
const resultRequiredEnd = formatRequest(`GET /api/endpoint/{param} ${version}`, pathParams);
|
||||
expect(resultRequiredEnd.pathname).toBe('/api/endpoint/testParam');
|
||||
});
|
||||
it('should return the correct path if the only an optional param is provided', () => {
|
||||
const resultOptEnd = formatRequest(`GET /api/endpoint/{id?} ${version}`, { id: 123 });
|
||||
expect(resultOptEnd.pathname).toBe('/api/endpoint/123');
|
||||
});
|
||||
it('should return the correct path if the optional param is not provided', () => {
|
||||
const pathParams = {};
|
||||
const resultEnd = formatRequest(`GET /api/endpoint/{pathParamReq?} ${version}`, pathParams);
|
||||
expect(resultEnd.pathname).toBe('/api/endpoint');
|
||||
});
|
||||
});
|
|
@ -11,11 +11,23 @@ import { parseEndpoint } from './parse_endpoint';
|
|||
|
||||
export function formatRequest(endpoint: string, pathParams: Record<string, any> = {}) {
|
||||
const { method, pathname: rawPathname, version } = parseEndpoint(endpoint);
|
||||
const optionalReg = /(\/\{\w+\?\})/g; // /{param?}
|
||||
|
||||
const optionalOrRequiredParamsReg = /(\/{)((.+?))(\})/g;
|
||||
if (Object.keys(pathParams)?.length === 0) {
|
||||
const pathname = rawPathname.replace(optionalOrRequiredParamsReg, '');
|
||||
return { method, pathname, version };
|
||||
}
|
||||
|
||||
// replace template variables with path params
|
||||
const pathname = Object.keys(pathParams).reduce((acc, paramName) => {
|
||||
return acc.replace(`{${paramName}}`, pathParams[paramName]);
|
||||
return acc
|
||||
.replace(`{${paramName}}`, pathParams[paramName])
|
||||
.replace(`{${paramName}?}`, pathParams[paramName]);
|
||||
}, rawPathname);
|
||||
|
||||
if ((pathname.match(optionalReg) ?? [])?.length > 0) {
|
||||
throw new Error(`Missing parameters: ${pathname.match(optionalReg)}`);
|
||||
}
|
||||
|
||||
return { method, pathname, version };
|
||||
}
|
||||
|
|
|
@ -13,8 +13,9 @@ import {
|
|||
isHttpFetchError,
|
||||
} from '@kbn/server-route-repository-client';
|
||||
import { type KueryNode, nodeTypes, toKqlExpression } from '@kbn/es-query';
|
||||
import type { EntityInstance, EntityMetadata } from '@kbn/entities-schema';
|
||||
import type { EntityDefinition, EntityInstance, EntityMetadata } from '@kbn/entities-schema';
|
||||
import { castArray } from 'lodash';
|
||||
import type { EntityDefinitionWithState } from '../../server/lib/entities/types';
|
||||
import {
|
||||
DisableManagedEntityResponse,
|
||||
EnableManagedEntityResponse,
|
||||
|
@ -87,6 +88,24 @@ export class EntityClient {
|
|||
}
|
||||
}
|
||||
|
||||
async getEntityDefinition(
|
||||
id: string
|
||||
): Promise<{ definitions: EntityDefinition[] | EntityDefinitionWithState[] }> {
|
||||
try {
|
||||
return await this.repositoryClient('GET /internal/entities/definition/{id?}', {
|
||||
params: {
|
||||
path: { id },
|
||||
query: { page: 1, perPage: 1 },
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
if (isHttpFetchError(err) && err.body?.statusCode === 403) {
|
||||
throw new EntityManagerUnauthorizedError(err.body.message);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
asKqlFilter(
|
||||
entityInstance: {
|
||||
entity: Pick<EntityInstance['entity'], 'identity_fields'>;
|
||||
|
|
|
@ -217,16 +217,16 @@ describe('Home page', () => {
|
|||
cy.intercept('GET', '/internal/entities/managed/enablement', {
|
||||
fixture: 'eem_enabled.json',
|
||||
}).as('getEEMStatus');
|
||||
cy.intercept('GET', '/internal/inventory/entities?**').as('getEntities');
|
||||
cy.visitKibana('/app/inventory');
|
||||
cy.wait('@getEEMStatus');
|
||||
cy.contains('container');
|
||||
cy.getByTestSubj('inventoryGroupTitle_entity.type_container').click();
|
||||
cy.wait('@getEntities');
|
||||
// cy.getByTestSubj('inventoryEntityActionsButton').click();
|
||||
cy.getByTestSubj('inventoryEntityActionsButton-foo').click();
|
||||
cy.getByTestSubj('inventoryEntityActionOpenInDiscover').click();
|
||||
cy.url().should(
|
||||
'include',
|
||||
"query:'container.id:%20%22foo%22%20AND%20entity.definition_id%20:%20builtin*"
|
||||
);
|
||||
cy.getByTestSubj('inventoryEntityActionExploreInDiscover').click();
|
||||
cy.url().should('include', "query:'container.id:%20%22foo%22");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -14,7 +14,7 @@ import {
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedDate, FormattedMessage, FormattedTime } from '@kbn/i18n-react';
|
||||
import { last } from 'lodash';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { ENTITY_TYPE } from '@kbn/observability-shared-plugin/common';
|
||||
import { EntityColumnIds, InventoryEntity } from '../../../common/entities';
|
||||
import { BadgeFilterWithPopover } from '../badge_filter_with_popover';
|
||||
|
@ -22,7 +22,6 @@ import { getColumns } from './grid_columns';
|
|||
import { AlertsBadge } from '../alerts_badge/alerts_badge';
|
||||
import { EntityName } from './entity_name';
|
||||
import { EntityActions } from '../entity_actions';
|
||||
import { useDiscoverRedirect } from '../../hooks/use_discover_redirect';
|
||||
|
||||
interface Props {
|
||||
loading: boolean;
|
||||
|
@ -45,7 +44,7 @@ export function EntitiesGrid({
|
|||
onChangePage,
|
||||
onChangeSort,
|
||||
}: Props) {
|
||||
const { getDiscoverRedirectUrl } = useDiscoverRedirect();
|
||||
const [showActions, setShowActions] = useState<boolean>(true);
|
||||
|
||||
const onSort: EuiDataGridSorting['onSort'] = useCallback(
|
||||
(newSortingColumns) => {
|
||||
|
@ -62,8 +61,6 @@ export function EntitiesGrid({
|
|||
[entities]
|
||||
);
|
||||
|
||||
const showActions = useMemo(() => !!getDiscoverRedirectUrl(), [getDiscoverRedirectUrl]);
|
||||
|
||||
const columnVisibility = useMemo(
|
||||
() => ({
|
||||
visibleColumns: getColumns({ showAlertsColumn, showActions }).map(({ id }) => id),
|
||||
|
@ -81,7 +78,6 @@ export function EntitiesGrid({
|
|||
|
||||
const columnEntityTableId = columnId as EntityColumnIds;
|
||||
const entityType = entity.entityType;
|
||||
const discoverUrl = getDiscoverRedirectUrl(entity);
|
||||
|
||||
switch (columnEntityTableId) {
|
||||
case 'alertsCount':
|
||||
|
@ -119,19 +115,12 @@ export function EntitiesGrid({
|
|||
case 'entityDisplayName':
|
||||
return <EntityName entity={entity} />;
|
||||
case 'actions':
|
||||
return (
|
||||
discoverUrl && (
|
||||
<EntityActions
|
||||
discoverUrl={discoverUrl}
|
||||
entityIdentifyingValue={entity.entityDisplayName}
|
||||
/>
|
||||
)
|
||||
);
|
||||
return <EntityActions entity={entity} setShowActions={setShowActions} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
},
|
||||
[entities, getDiscoverRedirectUrl]
|
||||
[entities]
|
||||
);
|
||||
|
||||
if (loading) {
|
||||
|
|
|
@ -7,56 +7,70 @@
|
|||
|
||||
import { EuiButtonIcon, EuiContextMenuItem, EuiContextMenuPanel, EuiPopover } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import React, { type SetStateAction } from 'react';
|
||||
import { useBoolean } from '@kbn/react-hooks';
|
||||
import type { Dispatch } from '@kbn/kibana-utils-plugin/common';
|
||||
import type { InventoryEntity } from '../../../common/entities';
|
||||
import { useDiscoverRedirect } from '../../hooks/use_discover_redirect';
|
||||
|
||||
interface Props {
|
||||
discoverUrl: string;
|
||||
entityIdentifyingValue?: string;
|
||||
entity: InventoryEntity;
|
||||
setShowActions: Dispatch<SetStateAction<boolean>>;
|
||||
}
|
||||
|
||||
export const EntityActions = ({ discoverUrl, entityIdentifyingValue }: Props) => {
|
||||
export const EntityActions = ({ entity, setShowActions }: Props) => {
|
||||
const [isPopoverOpen, { toggle: togglePopover, off: closePopover }] = useBoolean(false);
|
||||
const actionButtonTestSubject = entityIdentifyingValue
|
||||
? `inventoryEntityActionsButton-${entityIdentifyingValue}`
|
||||
const actionButtonTestSubject = entity.entityDisplayName
|
||||
? `inventoryEntityActionsButton-${entity.entityDisplayName}`
|
||||
: 'inventoryEntityActionsButton';
|
||||
|
||||
const actions = [
|
||||
<EuiContextMenuItem
|
||||
data-test-subj="inventoryEntityActionOpenInDiscover"
|
||||
key={`openInDiscover-${entityIdentifyingValue}`}
|
||||
color="text"
|
||||
icon="discoverApp"
|
||||
href={discoverUrl}
|
||||
>
|
||||
{i18n.translate('xpack.inventory.entityActions.discoverLink', {
|
||||
defaultMessage: 'Open in discover',
|
||||
})}
|
||||
</EuiContextMenuItem>,
|
||||
];
|
||||
const { getDiscoverEntitiesRedirectUrl, isEntityDefinitionLoading } = useDiscoverRedirect(entity);
|
||||
const discoverUrl = getDiscoverEntitiesRedirectUrl();
|
||||
|
||||
const actions: React.ReactElement[] = [];
|
||||
|
||||
if (!discoverUrl && !isEntityDefinitionLoading) {
|
||||
setShowActions(false);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!isEntityDefinitionLoading) {
|
||||
actions.push(
|
||||
<EuiContextMenuItem
|
||||
data-test-subj="inventoryEntityActionExploreInDiscover"
|
||||
key={`exploreInDiscover-${entity.entityDisplayName}`}
|
||||
color="text"
|
||||
icon="discoverApp"
|
||||
href={discoverUrl}
|
||||
>
|
||||
{i18n.translate('xpack.inventory.entityActions.exploreInDiscoverLink', {
|
||||
defaultMessage: 'Explore in Discover',
|
||||
})}
|
||||
</EuiContextMenuItem>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiPopover
|
||||
isOpen={isPopoverOpen}
|
||||
panelPaddingSize="none"
|
||||
anchorPosition="upCenter"
|
||||
button={
|
||||
<EuiButtonIcon
|
||||
data-test-subj={actionButtonTestSubject}
|
||||
aria-label={i18n.translate(
|
||||
'xpack.inventory.entityActions.euiButtonIcon.showActionsLabel',
|
||||
{ defaultMessage: 'Show actions' }
|
||||
)}
|
||||
iconType="boxesHorizontal"
|
||||
color="text"
|
||||
onClick={togglePopover}
|
||||
/>
|
||||
}
|
||||
closePopover={closePopover}
|
||||
>
|
||||
<EuiContextMenuPanel items={actions} size="s" />
|
||||
</EuiPopover>
|
||||
</>
|
||||
<EuiPopover
|
||||
isOpen={isPopoverOpen}
|
||||
panelPaddingSize="none"
|
||||
anchorPosition="upCenter"
|
||||
button={
|
||||
<EuiButtonIcon
|
||||
data-test-subj={actionButtonTestSubject}
|
||||
aria-label={i18n.translate(
|
||||
'xpack.inventory.entityActions.euiButtonIcon.showActionsLabel',
|
||||
{ defaultMessage: 'Show actions' }
|
||||
)}
|
||||
iconType="boxesHorizontal"
|
||||
color="text"
|
||||
onClick={togglePopover}
|
||||
isLoading={isEntityDefinitionLoading}
|
||||
/>
|
||||
}
|
||||
closePopover={closePopover}
|
||||
>
|
||||
<EuiContextMenuPanel items={actions} size="s" />
|
||||
</EuiPopover>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
import React, { createContext, useContext, type ReactChild } from 'react';
|
||||
import { Subject } from 'rxjs';
|
||||
import { DataView } from '@kbn/data-views-plugin/common';
|
||||
import { useAdHocInventoryDataView } from '../../hooks/use_adhoc_inventory_data_view';
|
||||
import { ENTITIES_LATEST_ALIAS } from '../../../common/entities';
|
||||
import { useAdHocDataView } from '../../hooks/use_adhoc_data_view';
|
||||
|
||||
interface InventorySearchBarContextType {
|
||||
searchBarContentSubject$: Subject<{
|
||||
|
@ -24,7 +25,7 @@ const InventorySearchBarContext = createContext<InventorySearchBarContextType>({
|
|||
});
|
||||
|
||||
export function InventorySearchBarContextProvider({ children }: { children: ReactChild }) {
|
||||
const { dataView } = useAdHocInventoryDataView();
|
||||
const { dataView } = useAdHocDataView(ENTITIES_LATEST_ALIAS);
|
||||
return (
|
||||
<InventorySearchBarContext.Provider
|
||||
value={{
|
||||
|
|
|
@ -9,9 +9,8 @@ import { DataView } from '@kbn/data-views-plugin/common';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useKibana } from './use_kibana';
|
||||
import { ENTITIES_LATEST_ALIAS } from '../../common/entities';
|
||||
|
||||
export function useAdHocInventoryDataView() {
|
||||
export function useAdHocDataView(title: string) {
|
||||
const {
|
||||
services: { dataViews, notifications },
|
||||
} = useKibana();
|
||||
|
@ -21,7 +20,7 @@ export function useAdHocInventoryDataView() {
|
|||
async function fetchDataView() {
|
||||
try {
|
||||
const displayError = false;
|
||||
return await dataViews.create({ title: ENTITIES_LATEST_ALIAS }, undefined, displayError);
|
||||
return await dataViews.create({ title }, undefined, displayError);
|
||||
} catch (e) {
|
||||
const noDataScreen = e.message.includes('No matching indices found');
|
||||
if (noDataScreen) {
|
||||
|
@ -40,7 +39,7 @@ export function useAdHocInventoryDataView() {
|
|||
}
|
||||
|
||||
fetchDataView().then(setDataView);
|
||||
}, [dataViews, notifications.toasts]);
|
||||
}, [dataViews, title, notifications.toasts]);
|
||||
|
||||
return { dataView };
|
||||
}
|
|
@ -4,74 +4,55 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import {
|
||||
ENTITY_DEFINITION_ID,
|
||||
ENTITY_DISPLAY_NAME,
|
||||
ENTITY_LAST_SEEN,
|
||||
ENTITY_TYPE,
|
||||
} from '@kbn/observability-shared-plugin/common';
|
||||
import { useCallback } from 'react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import type { InventoryEntity } from '../../common/entities';
|
||||
import { useKibana } from './use_kibana';
|
||||
import { useUnifiedSearchContext } from './use_unified_search_context';
|
||||
import { useFetchEntityDefinition } from './use_fetch_entity_definition';
|
||||
import { useAdHocDataView } from './use_adhoc_data_view';
|
||||
|
||||
const ACTIVE_COLUMNS = [ENTITY_DISPLAY_NAME, ENTITY_TYPE, ENTITY_LAST_SEEN];
|
||||
|
||||
export const useDiscoverRedirect = () => {
|
||||
export const useDiscoverRedirect = (entity: InventoryEntity) => {
|
||||
const {
|
||||
services: { share, application, entityManager },
|
||||
} = useKibana();
|
||||
const { entityDefinitions, isEntityDefinitionLoading } = useFetchEntityDefinition(
|
||||
entity.entityDefinitionId
|
||||
);
|
||||
|
||||
const {
|
||||
dataView,
|
||||
searchState: { query, filters, panelFilters },
|
||||
} = useUnifiedSearchContext();
|
||||
const title = useMemo(
|
||||
() =>
|
||||
!isEntityDefinitionLoading && entityDefinitions && entityDefinitions?.length > 0
|
||||
? entityDefinitions[0]?.indexPatterns?.join(',')
|
||||
: '',
|
||||
[entityDefinitions, isEntityDefinitionLoading]
|
||||
);
|
||||
|
||||
const { dataView } = useAdHocDataView(title);
|
||||
|
||||
const discoverLocator = share.url.locators.get('DISCOVER_APP_LOCATOR');
|
||||
|
||||
const getDiscoverEntitiesRedirectUrl = useCallback(
|
||||
(entity?: InventoryEntity) => {
|
||||
const entityKqlFilter = entity
|
||||
? entityManager.entityClient.asKqlFilter({
|
||||
entity: {
|
||||
identity_fields: entity.entityIdentityFields,
|
||||
},
|
||||
...entity,
|
||||
})
|
||||
: '';
|
||||
const getDiscoverEntitiesRedirectUrl = useCallback(() => {
|
||||
const entityKqlFilter = entity
|
||||
? entityManager.entityClient.asKqlFilter({
|
||||
entity: {
|
||||
identity_fields: entity.entityIdentityFields,
|
||||
},
|
||||
...entity,
|
||||
})
|
||||
: '';
|
||||
|
||||
const kueryWithEntityDefinitionFilters = [
|
||||
query.query,
|
||||
entityKqlFilter,
|
||||
`${ENTITY_DEFINITION_ID} : builtin*`,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' AND ');
|
||||
return application.capabilities.discover?.show
|
||||
? discoverLocator?.getRedirectUrl({
|
||||
indexPatternId: dataView?.id ?? '',
|
||||
query: { query: entityKqlFilter, language: 'kuery' },
|
||||
})
|
||||
: undefined;
|
||||
}, [
|
||||
application.capabilities.discover?.show,
|
||||
dataView?.id,
|
||||
discoverLocator,
|
||||
entity,
|
||||
entityManager.entityClient,
|
||||
]);
|
||||
|
||||
return application.capabilities.discover?.show
|
||||
? discoverLocator?.getRedirectUrl({
|
||||
indexPatternId: dataView?.id ?? '',
|
||||
columns: ACTIVE_COLUMNS,
|
||||
query: { query: kueryWithEntityDefinitionFilters, language: 'kuery' },
|
||||
filters: [...filters, ...panelFilters],
|
||||
})
|
||||
: undefined;
|
||||
},
|
||||
[
|
||||
application.capabilities.discover?.show,
|
||||
dataView?.id,
|
||||
discoverLocator,
|
||||
entityManager.entityClient,
|
||||
filters,
|
||||
panelFilters,
|
||||
query.query,
|
||||
]
|
||||
);
|
||||
|
||||
const getDiscoverRedirectUrl = useCallback(
|
||||
(entity?: InventoryEntity) => getDiscoverEntitiesRedirectUrl(entity),
|
||||
[getDiscoverEntitiesRedirectUrl]
|
||||
);
|
||||
|
||||
return { getDiscoverRedirectUrl };
|
||||
return { getDiscoverEntitiesRedirectUrl, isEntityDefinitionLoading };
|
||||
};
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { useAbortableAsync } from '@kbn/observability-utils-browser/hooks/use_abortable_async';
|
||||
import { useKibana } from './use_kibana';
|
||||
|
||||
export const useFetchEntityDefinition = (id: string) => {
|
||||
const {
|
||||
services: { entityManager },
|
||||
} = useKibana();
|
||||
|
||||
const { value, loading } = useAbortableAsync(
|
||||
({ signal }) => {
|
||||
return entityManager.entityClient.getEntityDefinition(id);
|
||||
},
|
||||
[entityManager.entityClient, id]
|
||||
);
|
||||
|
||||
return {
|
||||
entityDefinitions: value?.definitions,
|
||||
isEntityDefinitionLoading: loading,
|
||||
};
|
||||
};
|
|
@ -13,13 +13,14 @@ import useEffectOnce from 'react-use/lib/useEffectOnce';
|
|||
import deepEqual from 'fast-deep-equal';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useKibanaQuerySettings } from '@kbn/observability-shared-plugin/public';
|
||||
import { useAdHocInventoryDataView } from './use_adhoc_inventory_data_view';
|
||||
import { ENTITIES_LATEST_ALIAS } from '../../common/entities';
|
||||
import { useAdHocDataView } from './use_adhoc_data_view';
|
||||
import { useUnifiedSearchUrl } from './use_unified_search_url';
|
||||
import { useKibana } from './use_kibana';
|
||||
|
||||
function useUnifiedSearch() {
|
||||
const [isControlPanelsInitiated, setIsControlPanelsInitiated] = useState(false);
|
||||
const { dataView } = useAdHocInventoryDataView();
|
||||
const { dataView } = useAdHocDataView(ENTITIES_LATEST_ALIAS);
|
||||
const [refreshSubject$] = useState<Subject<void>>(new Subject());
|
||||
const { searchState, setSearchState } = useUnifiedSearchUrl();
|
||||
const kibanaQuerySettings = useKibanaQuerySettings();
|
||||
|
|
|
@ -59,6 +59,7 @@
|
|||
"@kbn/react-hooks",
|
||||
"@kbn/observability-utils-common",
|
||||
"@kbn/observability-utils-browser",
|
||||
"@kbn/observability-utils-server"
|
||||
"@kbn/observability-utils-server",
|
||||
"@kbn/kibana-utils-plugin"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -26204,7 +26204,6 @@
|
|||
"xpack.inventory.entitiesGrid.euiDataGrid.lastSeenTooltip": "Horodatage des dernières données reçues pour l'entité (entity.lastSeenTimestamp)",
|
||||
"xpack.inventory.entitiesGrid.euiDataGrid.typeLabel": "Type",
|
||||
"xpack.inventory.entitiesGrid.euiDataGrid.typeTooltip": "Type d'entité (entity.type)",
|
||||
"xpack.inventory.entityActions.discoverLink": "Ouvrir dans Discover",
|
||||
"xpack.inventory.featureRegistry.inventoryFeatureName": "Inventory",
|
||||
"xpack.inventory.home.serviceAlertsTable.tooltip.activeAlertsExplanation": "Alertes actives",
|
||||
"xpack.inventory.inventoryLinkTitle": "Inventory",
|
||||
|
|
|
@ -26176,7 +26176,6 @@
|
|||
"xpack.inventory.entitiesGrid.euiDataGrid.lastSeenTooltip": "エンティティで最後に受信したデータのタイムスタンプ(entity.lastSeenTimestamp)",
|
||||
"xpack.inventory.entitiesGrid.euiDataGrid.typeLabel": "型",
|
||||
"xpack.inventory.entitiesGrid.euiDataGrid.typeTooltip": "エンティティのタイプ(entity.type)",
|
||||
"xpack.inventory.entityActions.discoverLink": "Discoverで開く",
|
||||
"xpack.inventory.featureRegistry.inventoryFeatureName": "インベントリ",
|
||||
"xpack.inventory.home.serviceAlertsTable.tooltip.activeAlertsExplanation": "アクティブアラート",
|
||||
"xpack.inventory.inventoryLinkTitle": "インベントリ",
|
||||
|
|
|
@ -26232,7 +26232,6 @@
|
|||
"xpack.inventory.entitiesGrid.euiDataGrid.lastSeenTooltip": "上次接收的实体数据的时间戳 (entity.lastSeenTimestamp)",
|
||||
"xpack.inventory.entitiesGrid.euiDataGrid.typeLabel": "类型",
|
||||
"xpack.inventory.entitiesGrid.euiDataGrid.typeTooltip": "实体的类型 (entity.type)",
|
||||
"xpack.inventory.entityActions.discoverLink": "在 Discover 中打开",
|
||||
"xpack.inventory.featureRegistry.inventoryFeatureName": "库存",
|
||||
"xpack.inventory.home.serviceAlertsTable.tooltip.activeAlertsExplanation": "活动告警",
|
||||
"xpack.inventory.inventoryLinkTitle": "库存",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue