Skip to content

Commit

Permalink
[8.x] [Inventory] Change discover link to use entity definition (#201433
Browse files Browse the repository at this point in the history
) (#202154)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Inventory] Change discover link to use entity definition
(#201433)](#201433)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT
[{"author":{"name":"jennypavlova","email":"[email protected]"},"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\nhttps://github.com/user-attachments/assets/29bb46b4-719e-4874-b1aa-056ac28d234a\r\n\r\n\r\n\r\nhttps://github.com/user-attachments/assets/36c08b8b-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
<[email protected]>","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\nhttps://github.com/user-attachments/assets/29bb46b4-719e-4874-b1aa-056ac28d234a\r\n\r\n\r\n\r\nhttps://github.com/user-attachments/assets/36c08b8b-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
<[email protected]>","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\nhttps://github.com/user-attachments/assets/29bb46b4-719e-4874-b1aa-056ac28d234a\r\n\r\n\r\n\r\nhttps://github.com/user-attachments/assets/36c08b8b-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
<[email protected]>","sha":"afc5e51e296eb499a17ad82de560d20c352cd2a9"}},{"branch":"8.17","label":"v8.17.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

---------

Co-authored-by: jennypavlova <[email protected]>
  • Loading branch information
kibanamachine and jennypavlova authored Nov 29, 2024
1 parent 6289b6c commit 5036fd6
Show file tree
Hide file tree
Showing 15 changed files with 207 additions and 136 deletions.
Original file line number Diff line number Diff line change
@@ -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');
});
});
16 changes: 14 additions & 2 deletions packages/kbn-server-route-repository-utils/src/format_request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
}
21 changes: 20 additions & 1 deletion x-pack/plugins/entity_manager/public/lib/entity_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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'>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
});
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,14 @@ 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';
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;
Expand All @@ -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) => {
Expand All @@ -62,8 +61,6 @@ export function EntitiesGrid({
[entities]
);

const showActions = useMemo(() => !!getDiscoverRedirectUrl(), [getDiscoverRedirectUrl]);

const columnVisibility = useMemo(
() => ({
visibleColumns: getColumns({ showAlertsColumn, showActions }).map(({ id }) => id),
Expand All @@ -81,7 +78,6 @@ export function EntitiesGrid({

const columnEntityTableId = columnId as EntityColumnIds;
const entityType = entity.entityType;
const discoverUrl = getDiscoverRedirectUrl(entity);

switch (columnEntityTableId) {
case 'alertsCount':
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();

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}
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}
>
<EuiContextMenuPanel items={actions} size="s" />
</EuiPopover>
</>
{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}
isLoading={isEntityDefinitionLoading}
/>
}
closePopover={closePopover}
>
<EuiContextMenuPanel items={actions} size="s" />
</EuiPopover>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -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<{
Expand All @@ -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={{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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) {
Expand All @@ -40,7 +39,7 @@ export function useAdHocInventoryDataView() {
}

fetchDataView().then(setDataView);
}, [dataViews, notifications.toasts]);
}, [dataViews, title, notifications.toasts]);

return { dataView };
}
Loading

0 comments on commit 5036fd6

Please sign in to comment.