[8.17] [Inventory] Change discover link to use entity definition (#201433) (#202153)

# 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:
Kibana Machine 2024-11-30 01:23:46 +11:00 committed by GitHub
parent 8635c81ad0
commit dffa1d0518
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 205 additions and 134 deletions

View file

@ -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');
});
});

View file

@ -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 };
}

View file

@ -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'>;

View file

@ -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");
});
});
});

View file

@ -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) {

View file

@ -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>
);
};

View file

@ -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={{

View file

@ -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 };
}

View file

@ -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 };
};

View file

@ -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,
};
};

View file

@ -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();

View file

@ -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"
]
}

View file

@ -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",

View file

@ -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": "インベントリ",

View file

@ -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": "库存",