From ffccfdc62cd1da5baf54568cbee20a9a3466178e Mon Sep 17 00:00:00 2001
From: jennypavlova <dzheni.pavlova@elastic.co>
Date: Tue, 14 Jan 2025 16:30:39 +0100
Subject: [PATCH] [ECO][Inventory v2] Ad hoc data view: Add get entities
 definition endpoint using sources (#204026)

Closes #202298

This PR changes the way we get the entity index patterns to v2. It
creates an endpoint part of the inventory API which returns the index
patterns by entity type.

## Testing

### Test the endpoint:
- Open Dev tools and add
` GET kbn:/internal/inventory/entity/definitions/sources`
- Response:


![image](https://github.com/user-attachments/assets/3346c36e-dbc2-4e56-9ed6-d3d3a8f7d1a5)


### Test in the UI
- After the previous steps add some host data (oblt cluster /
metricbeat) or use synthtrace (for example use `node scripts/synthtrace
infra_hosts_with_apm_hosts --scenarioOpts.numInstances=10` or `node
scripts/synthtrace logs_traces_hosts.ts`)
- Go to Inventory and expand the host group
- Click on the actions button for any host and click on the Discover
link
- The correct dataview should be selected based on the index patterns in
the source definition
The same can be done for other entity types
- Test the search bar as well (the suggestions should be visible) and
now we should have 1 request to get the sources (instead of doing it on
click)



https://github.com/user-attachments/assets/93b5ac6c-9d64-44e0-b26e-6133477e0840




<!--ONMERGE {"backportTargets":["8.x"]} ONMERGE-->

---------

Co-authored-by: Carlos Crespo <crespocarlos@users.noreply.github.com>
Co-authored-by: Sergi Romeu <sergi.romeu@elastic.co>
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
---
 .../components/entity_actions/index.tsx       |  35 ++--
 .../entity_group_accordion/index.tsx          |   5 +-
 .../public/hooks/use_discover_redirect.ts     |  29 +--
 ..._fetch_entity_definition_index_patterns.ts |  28 +++
 .../hooks/use_unified_search_context.ts       |  61 ++++++-
 .../public/pages/inventory_page/index.tsx     |  30 +--
 ...ty_index_patterns_from_definitions.test.ts | 171 ++++++++++++++++++
 ..._entity_index_patterns_from_definitions.ts |  19 ++
 .../get_entity_definitions.ts                 |  32 ++++
 .../get_global_inventory_route_repository.ts  |   2 +
 10 files changed, 338 insertions(+), 74 deletions(-)
 create mode 100644 x-pack/solutions/observability/plugins/inventory/public/hooks/use_fetch_entity_definition_index_patterns.ts
 create mode 100644 x-pack/solutions/observability/plugins/inventory/server/routes/entity_definition/extract_entity_index_patterns_from_definitions.test.ts
 create mode 100644 x-pack/solutions/observability/plugins/inventory/server/routes/entity_definition/extract_entity_index_patterns_from_definitions.ts
 create mode 100644 x-pack/solutions/observability/plugins/inventory/server/routes/entity_definition/get_entity_definitions.ts

diff --git a/x-pack/solutions/observability/plugins/inventory/public/components/entity_actions/index.tsx b/x-pack/solutions/observability/plugins/inventory/public/components/entity_actions/index.tsx
index 691ba4388ac63..cd76b068e7e40 100644
--- a/x-pack/solutions/observability/plugins/inventory/public/components/entity_actions/index.tsx
+++ b/x-pack/solutions/observability/plugins/inventory/public/components/entity_actions/index.tsx
@@ -24,32 +24,28 @@ export const EntityActions = ({ entity, setShowActions }: Props) => {
     ? `inventoryEntityActionsButton-${entity.entityDisplayName}`
     : 'inventoryEntityActionsButton';
 
-  const { getDiscoverEntitiesRedirectUrl, isEntityDefinitionLoading } = useDiscoverRedirect(entity);
+  const { getDiscoverEntitiesRedirectUrl } = useDiscoverRedirect(entity);
   const discoverUrl = getDiscoverEntitiesRedirectUrl();
 
-  const actions: React.ReactElement[] = [];
+  const actions = [
+    <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>,
+  ];
 
-  if (!discoverUrl && !isEntityDefinitionLoading) {
+  if (!discoverUrl) {
     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}
@@ -65,7 +61,6 @@ export const EntityActions = ({ entity, setShowActions }: Props) => {
           iconType="boxesHorizontal"
           color="text"
           onClick={togglePopover}
-          isLoading={isEntityDefinitionLoading}
         />
       }
       closePopover={closePopover}
diff --git a/x-pack/solutions/observability/plugins/inventory/public/components/entity_group_accordion/index.tsx b/x-pack/solutions/observability/plugins/inventory/public/components/entity_group_accordion/index.tsx
index d1f95e794ea1b..503e30f98dac3 100644
--- a/x-pack/solutions/observability/plugins/inventory/public/components/entity_group_accordion/index.tsx
+++ b/x-pack/solutions/observability/plugins/inventory/public/components/entity_group_accordion/index.tsx
@@ -10,6 +10,7 @@ import { i18n } from '@kbn/i18n';
 import React, { useCallback, useState } from 'react';
 import { EntityCountBadge } from './entity_count_badge';
 import { GroupedEntitiesGrid } from './grouped_entities_grid';
+import { useUnifiedSearchContext } from '../../hooks/use_unified_search_context';
 
 const ENTITIES_COUNT_BADGE = i18n.translate(
   'xpack.inventory.inventoryGroupPanel.entitiesBadgeLabel',
@@ -26,10 +27,12 @@ export interface Props {
 export function EntityGroupAccordion({ groupValue, groupLabel, groupCount, isLoading }: Props) {
   const { euiTheme } = useEuiTheme();
   const [open, setOpen] = useState(false);
+  const { setSingleEntityType } = useUnifiedSearchContext();
 
   const onToggle = useCallback(() => {
+    if (!open) setSingleEntityType(groupValue);
     setOpen((opened) => !opened);
-  }, []);
+  }, [groupValue, open, setSingleEntityType]);
 
   return (
     <>
diff --git a/x-pack/solutions/observability/plugins/inventory/public/hooks/use_discover_redirect.ts b/x-pack/solutions/observability/plugins/inventory/public/hooks/use_discover_redirect.ts
index 141b6d2fc97bc..b42359b70d7d7 100644
--- a/x-pack/solutions/observability/plugins/inventory/public/hooks/use_discover_redirect.ts
+++ b/x-pack/solutions/observability/plugins/inventory/public/hooks/use_discover_redirect.ts
@@ -4,30 +4,17 @@
  * 2.0; you may not use this file except in compliance with the Elastic License
  * 2.0.
  */
-import { useCallback, useMemo } from 'react';
+import { useCallback } from 'react';
 import type { InventoryEntity } from '../../common/entities';
-import { useAdHocDataView } from './use_adhoc_data_view';
-import { useFetchEntityDefinition } from './use_fetch_entity_definition';
 import { useKibana } from './use_kibana';
+import { useUnifiedSearchContext } from './use_unified_search_context';
 
 export const useDiscoverRedirect = (entity: InventoryEntity) => {
   const {
     services: { share, application, entityManager },
   } = useKibana();
-  const { entityDefinitions, isEntityDefinitionLoading } = useFetchEntityDefinition(
-    entity.entityDefinitionId as string
-  );
-
-  const title = useMemo(
-    () =>
-      !isEntityDefinitionLoading && entityDefinitions && entityDefinitions?.length > 0
-        ? entityDefinitions[0]?.indexPatterns?.join(',')
-        : '',
-    [entityDefinitions, isEntityDefinitionLoading]
-  );
-
-  const { dataView } = useAdHocDataView(title);
-
+  const { discoverDataview } = useUnifiedSearchContext();
+  const { dataView } = discoverDataview;
   const discoverLocator = share.url.locators.get('DISCOVER_APP_LOCATOR');
 
   const getDiscoverEntitiesRedirectUrl = useCallback(() => {
@@ -37,19 +24,19 @@ export const useDiscoverRedirect = (entity: InventoryEntity) => {
         })
       : '';
 
-    return application.capabilities.discover?.show
+    return application.capabilities.discover?.show || !dataView
       ? discoverLocator?.getRedirectUrl({
-          indexPatternId: dataView?.id ?? '',
+          dataViewId: dataView?.id ?? '',
           query: { query: entityKqlFilter, language: 'kuery' },
         })
       : undefined;
   }, [
     application.capabilities.discover?.show,
-    dataView?.id,
+    dataView,
     discoverLocator,
     entity,
     entityManager.entityClient,
   ]);
 
-  return { getDiscoverEntitiesRedirectUrl, isEntityDefinitionLoading };
+  return { getDiscoverEntitiesRedirectUrl };
 };
diff --git a/x-pack/solutions/observability/plugins/inventory/public/hooks/use_fetch_entity_definition_index_patterns.ts b/x-pack/solutions/observability/plugins/inventory/public/hooks/use_fetch_entity_definition_index_patterns.ts
new file mode 100644
index 0000000000000..d275ccdc10c67
--- /dev/null
+++ b/x-pack/solutions/observability/plugins/inventory/public/hooks/use_fetch_entity_definition_index_patterns.ts
@@ -0,0 +1,28 @@
+/*
+ * 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 { useInventoryAbortableAsync } from './use_inventory_abortable_async';
+import { useKibana } from './use_kibana';
+
+export const useFetchEntityDefinitionIndexPattern = () => {
+  const {
+    services: { inventoryAPIClient },
+  } = useKibana();
+
+  const { value = { definitionIndexPatterns: {} }, loading } = useInventoryAbortableAsync(
+    ({ signal }) => {
+      return inventoryAPIClient.fetch('GET /internal/inventory/entity/definitions/sources', {
+        signal,
+      });
+    },
+    [inventoryAPIClient]
+  );
+
+  return {
+    definitionIndexPatterns: value?.definitionIndexPatterns,
+    isIndexPatternsLoading: loading,
+  };
+};
diff --git a/x-pack/solutions/observability/plugins/inventory/public/hooks/use_unified_search_context.ts b/x-pack/solutions/observability/plugins/inventory/public/hooks/use_unified_search_context.ts
index c5715fb1d50b6..c053f868f2eb0 100644
--- a/x-pack/solutions/observability/plugins/inventory/public/hooks/use_unified_search_context.ts
+++ b/x-pack/solutions/observability/plugins/inventory/public/hooks/use_unified_search_context.ts
@@ -5,18 +5,73 @@
  * 2.0.
  */
 import createContainer from 'constate';
-import { useState } from 'react';
+import { useMemo, useState } from 'react';
 import { Subject } from 'rxjs';
-import { ENTITIES_LATEST_ALIAS } from '../../common/entities';
 import { useAdHocDataView } from './use_adhoc_data_view';
+import { useInventoryDecodedQueryParams } from './use_inventory_decoded_query_params';
+import { useInventoryAbortableAsync } from './use_inventory_abortable_async';
+import { groupEntityTypesByStatus } from '../utils/group_entity_types_by_status';
+import { useKibana } from './use_kibana';
+import { useInventoryParams } from './use_inventory_params';
+import { useFetchEntityDefinitionIndexPattern } from './use_fetch_entity_definition_index_patterns';
 
 function useUnifiedSearch() {
-  const { dataView } = useAdHocDataView(ENTITIES_LATEST_ALIAS);
+  const {
+    services: { inventoryAPIClient },
+  } = useKibana();
+  const {
+    query: { kuery },
+  } = useInventoryParams('/');
+  const { entityTypes } = useInventoryDecodedQueryParams();
+  const { definitionIndexPatterns, isIndexPatternsLoading } =
+    useFetchEntityDefinitionIndexPattern();
+  const [singleEntityType, setSingleEntityType] = useState<string>('');
+
+  const { value, refresh, loading } = useInventoryAbortableAsync(
+    ({ signal }) => {
+      const { entityTypesOff, entityTypesOn } = groupEntityTypesByStatus(entityTypes);
+      return inventoryAPIClient.fetch('GET /internal/inventory/entities/types', {
+        params: {
+          query: {
+            includeEntityTypes: entityTypesOn.length ? JSON.stringify(entityTypesOn) : undefined,
+            excludeEntityTypes: entityTypesOff.length ? JSON.stringify(entityTypesOff) : undefined,
+            kuery,
+          },
+        },
+        signal,
+      });
+    },
+    [entityTypes, inventoryAPIClient, kuery]
+  );
+
+  const entityTypeIds = useMemo(
+    () => value?.entityTypes.map((entityType) => entityType.id) ?? [],
+    [value?.entityTypes]
+  );
+  const allDefinitionIndexPatterns = useMemo(() => {
+    const filteredDefinitionIndexPatterns = entityTypeIds.flatMap(
+      (id) => definitionIndexPatterns?.[id] ?? []
+    );
+
+    return Array.from(new Set(filteredDefinitionIndexPatterns)).join(',');
+  }, [definitionIndexPatterns, entityTypeIds]);
+
+  const { dataView } = useAdHocDataView(allDefinitionIndexPatterns ?? '');
+  const discoverDataview = useAdHocDataView(
+    (definitionIndexPatterns[singleEntityType ?? ''] ?? []).join(',') ?? ''
+  );
+
   const [refreshSubject$] = useState<Subject<void>>(new Subject());
 
   return {
     dataView,
+    definitionIndexPatterns,
     refreshSubject$,
+    loading: loading || isIndexPatternsLoading,
+    refresh,
+    value,
+    discoverDataview,
+    setSingleEntityType,
   };
 }
 
diff --git a/x-pack/solutions/observability/plugins/inventory/public/pages/inventory_page/index.tsx b/x-pack/solutions/observability/plugins/inventory/public/pages/inventory_page/index.tsx
index 49fe1a965d94d..22722df071dee 100644
--- a/x-pack/solutions/observability/plugins/inventory/public/pages/inventory_page/index.tsx
+++ b/x-pack/solutions/observability/plugins/inventory/public/pages/inventory_page/index.tsx
@@ -9,39 +9,11 @@ import React from 'react';
 import useEffectOnce from 'react-use/lib/useEffectOnce';
 import { EntitiesSummary } from '../../components/entities_summary';
 import { EntityGroupAccordion } from '../../components/entity_group_accordion';
-import { useInventoryAbortableAsync } from '../../hooks/use_inventory_abortable_async';
-import { useInventoryDecodedQueryParams } from '../../hooks/use_inventory_decoded_query_params';
-import { useInventoryParams } from '../../hooks/use_inventory_params';
-import { useKibana } from '../../hooks/use_kibana';
 import { useUnifiedSearchContext } from '../../hooks/use_unified_search_context';
 import { GroupBySelector } from '../../components/group_by_selector';
-import { groupEntityTypesByStatus } from '../../utils/group_entity_types_by_status';
 
 export function InventoryPage() {
-  const {
-    services: { inventoryAPIClient },
-  } = useKibana();
-  const { refreshSubject$ } = useUnifiedSearchContext();
-  const {
-    query: { kuery },
-  } = useInventoryParams('/');
-  const { entityTypes } = useInventoryDecodedQueryParams();
-  const { value, refresh, loading } = useInventoryAbortableAsync(
-    ({ signal }) => {
-      const { entityTypesOff, entityTypesOn } = groupEntityTypesByStatus(entityTypes);
-      return inventoryAPIClient.fetch('GET /internal/inventory/entities/types', {
-        params: {
-          query: {
-            includeEntityTypes: entityTypesOn.length ? JSON.stringify(entityTypesOn) : undefined,
-            excludeEntityTypes: entityTypesOff.length ? JSON.stringify(entityTypesOff) : undefined,
-            kuery,
-          },
-        },
-        signal,
-      });
-    },
-    [entityTypes, inventoryAPIClient, kuery]
-  );
+  const { refreshSubject$, value, refresh, loading } = useUnifiedSearchContext();
 
   useEffectOnce(() => {
     const refreshSubscription = refreshSubject$.subscribe(refresh);
diff --git a/x-pack/solutions/observability/plugins/inventory/server/routes/entity_definition/extract_entity_index_patterns_from_definitions.test.ts b/x-pack/solutions/observability/plugins/inventory/server/routes/entity_definition/extract_entity_index_patterns_from_definitions.test.ts
new file mode 100644
index 0000000000000..80aadab75bc70
--- /dev/null
+++ b/x-pack/solutions/observability/plugins/inventory/server/routes/entity_definition/extract_entity_index_patterns_from_definitions.test.ts
@@ -0,0 +1,171 @@
+/*
+ * 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 type { EntitySourceDefinition } from '@kbn/entityManager-plugin/server/lib/v2/types';
+import { extractEntityIndexPatternsFromDefinitions } from './extract_entity_index_patterns_from_definitions';
+
+describe('extractEntityIndexPatternsFromDefinitions', () => {
+  it('should correctly extract index patterns for host entity types with single source', () => {
+    const sourceDefinition: EntitySourceDefinition[] = [
+      {
+        id: 'built_in_hosts_from_ecs_data_ecs',
+        type_id: 'built_in_hosts_from_ecs_data',
+        index_patterns: ['filebeat-*', 'logs-*', 'metrics-*', 'metricbeat-*'],
+        identity_fields: ['host.name'],
+        display_name: 'host.name',
+        timestamp_field: '@timestamp',
+        metadata_fields: [],
+        filters: [],
+      },
+    ];
+
+    const result = extractEntityIndexPatternsFromDefinitions(sourceDefinition);
+
+    expect(result).toEqual({
+      built_in_hosts_from_ecs_data: ['filebeat-*', 'logs-*', 'metrics-*', 'metricbeat-*'],
+    });
+  });
+  it('should correctly extract index patterns for service and host entity types with single source', () => {
+    const sourceDefinition: EntitySourceDefinition[] = [
+      {
+        id: 'built_in_hosts_from_ecs_data_ecs',
+        type_id: 'built_in_hosts_from_ecs_data',
+        index_patterns: ['filebeat-*', 'logs-*', 'metrics-*', 'metricbeat-*'],
+        identity_fields: ['host.name'],
+        display_name: 'host.name',
+        timestamp_field: '@timestamp',
+        metadata_fields: [],
+        filters: [],
+      },
+      {
+        id: 'built_in_services_from_ecs_data_ecs',
+        type_id: 'built_in_services_from_ecs_data',
+        index_patterns: ['logs-*', 'filebeat*', 'traces-*'],
+        identity_fields: ['service.name'],
+        display_name: 'service.name',
+        timestamp_field: '@timestamp',
+        metadata_fields: [],
+        filters: [],
+      },
+    ];
+
+    const result = extractEntityIndexPatternsFromDefinitions(sourceDefinition);
+
+    expect(result).toEqual({
+      built_in_hosts_from_ecs_data: ['filebeat-*', 'logs-*', 'metrics-*', 'metricbeat-*'],
+      built_in_services_from_ecs_data: ['logs-*', 'filebeat*', 'traces-*'],
+    });
+  });
+  it('should correctly extract index patterns for service and host entity types with multiple sources', () => {
+    const sourceDefinition: EntitySourceDefinition[] = [
+      {
+        id: 'built_in_hosts_from_ecs_data_ecs',
+        type_id: 'built_in_hosts_from_ecs_data',
+        index_patterns: ['metrics-*', 'metricbeat-*'],
+        identity_fields: ['host.name'],
+        display_name: 'host.name',
+        timestamp_field: '@timestamp',
+        metadata_fields: [],
+        filters: [],
+      },
+      {
+        id: 'built_in_hosts_from_ecs_data_ecs',
+        type_id: 'built_in_hosts_from_ecs_data',
+        index_patterns: ['filebeat-*', 'logs-*'],
+        identity_fields: ['host.name'],
+        display_name: 'host.name',
+        timestamp_field: '@timestamp',
+        metadata_fields: [],
+        filters: [],
+      },
+      {
+        id: 'built_in_services_from_ecs_data_ecs',
+        type_id: 'built_in_services_from_ecs_data',
+        index_patterns: ['logs-*'],
+        identity_fields: ['service.name'],
+        display_name: 'service.name',
+        timestamp_field: '@timestamp',
+        metadata_fields: [],
+        filters: [],
+      },
+      {
+        id: 'built_in_services_from_ecs_data_ecs',
+        type_id: 'built_in_services_from_ecs_data',
+        index_patterns: ['filebeat*', 'traces-*'],
+        identity_fields: ['service.name'],
+        display_name: 'service.name',
+        timestamp_field: '@timestamp',
+        metadata_fields: [],
+        filters: [],
+      },
+    ];
+
+    const result = extractEntityIndexPatternsFromDefinitions(sourceDefinition);
+
+    expect(result).toEqual({
+      built_in_hosts_from_ecs_data: ['metrics-*', 'metricbeat-*', 'filebeat-*', 'logs-*'],
+      built_in_services_from_ecs_data: ['logs-*', 'filebeat*', 'traces-*'],
+    });
+  });
+  it('should correctly extract index patterns for service and container entity types with multiple sources with duplicate patterns', () => {
+    const sourceDefinition: EntitySourceDefinition[] = [
+      {
+        id: 'built_in_hosts_from_ecs_data_ecs',
+        type_id: 'built_in_containers_from_ecs_data',
+        index_patterns: ['metrics-*', 'logs-*', 'metricbeat-*'],
+        identity_fields: ['host.name'],
+        display_name: 'host.name',
+        timestamp_field: '@timestamp',
+        metadata_fields: [],
+        filters: [],
+      },
+      {
+        id: 'built_in_containers_from_ecs_data_ecs_new_source',
+        type_id: 'built_in_containers_from_ecs_data',
+        index_patterns: ['metrics-*', 'filebeat-*', 'logs-*', 'new-*'],
+        identity_fields: ['host.name'],
+        display_name: 'host.name',
+        timestamp_field: '@timestamp',
+        metadata_fields: [],
+        filters: [],
+      },
+      {
+        id: 'built_in_services_from_ecs_data_ecs',
+        type_id: 'built_in_services_from_ecs_data',
+        index_patterns: ['logs-*', 'filebeat*', 'new-*'],
+        identity_fields: ['service.name'],
+        display_name: 'service.name',
+        timestamp_field: '@timestamp',
+        metadata_fields: [],
+        filters: [],
+      },
+      {
+        id: 'built_in_services_from_ecs_data_ecs_new_source',
+        type_id: 'built_in_services_from_ecs_data',
+        index_patterns: ['logs-*', 'filebeat*', 'traces-*'],
+        identity_fields: ['service.name'],
+        display_name: 'service.name',
+        timestamp_field: '@timestamp',
+        metadata_fields: [],
+        filters: [],
+      },
+    ];
+
+    const result = extractEntityIndexPatternsFromDefinitions(sourceDefinition);
+
+    expect(result).toEqual({
+      built_in_services_from_ecs_data: ['logs-*', 'filebeat*', 'new-*', 'traces-*'],
+      built_in_containers_from_ecs_data: [
+        'metrics-*',
+        'logs-*',
+        'metricbeat-*',
+        'filebeat-*',
+        'new-*',
+      ],
+    });
+  });
+});
diff --git a/x-pack/solutions/observability/plugins/inventory/server/routes/entity_definition/extract_entity_index_patterns_from_definitions.ts b/x-pack/solutions/observability/plugins/inventory/server/routes/entity_definition/extract_entity_index_patterns_from_definitions.ts
new file mode 100644
index 0000000000000..5b5fe4ba65d09
--- /dev/null
+++ b/x-pack/solutions/observability/plugins/inventory/server/routes/entity_definition/extract_entity_index_patterns_from_definitions.ts
@@ -0,0 +1,19 @@
+/*
+ * 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 type { EntitySourceDefinition } from '@kbn/entityManager-plugin/server/lib/v2/types';
+import { concat, uniq, compact } from 'lodash';
+
+export const extractEntityIndexPatternsFromDefinitions = (
+  entityDefinitionsSource: EntitySourceDefinition[]
+) =>
+  entityDefinitionsSource.reduce(
+    (acc, { ['type_id']: typeId, index_patterns: indexPatterns }) => (
+      (acc[typeId] = compact(uniq(concat(acc[typeId], indexPatterns)))), acc
+    ),
+    {} as Record<string, string[]>
+  );
diff --git a/x-pack/solutions/observability/plugins/inventory/server/routes/entity_definition/get_entity_definitions.ts b/x-pack/solutions/observability/plugins/inventory/server/routes/entity_definition/get_entity_definitions.ts
new file mode 100644
index 0000000000000..af1e335c215e5
--- /dev/null
+++ b/x-pack/solutions/observability/plugins/inventory/server/routes/entity_definition/get_entity_definitions.ts
@@ -0,0 +1,32 @@
+/*
+ * 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 { createInventoryServerRoute } from '../create_inventory_server_route';
+import { extractEntityIndexPatternsFromDefinitions } from './extract_entity_index_patterns_from_definitions';
+
+export const getEntityDefinitionSourceIndexPatternsByType = createInventoryServerRoute({
+  endpoint: 'GET /internal/inventory/entity/definitions/sources',
+  options: {
+    tags: ['access:inventory'],
+  },
+  async handler({ context, request, plugins }) {
+    const [_coreContext, entityManagerStart] = await Promise.all([
+      context.core,
+      plugins.entityManager.start(),
+    ]);
+    const entityManagerClient = await entityManagerStart.getScopedClient({ request });
+
+    const entityDefinitionsSource = await entityManagerClient.v2.readSourceDefinitions({});
+
+    return {
+      definitionIndexPatterns: extractEntityIndexPatternsFromDefinitions(entityDefinitionsSource),
+    };
+  },
+});
+
+export const entityDefinitionsRoutes = {
+  ...getEntityDefinitionSourceIndexPatternsByType,
+};
diff --git a/x-pack/solutions/observability/plugins/inventory/server/routes/get_global_inventory_route_repository.ts b/x-pack/solutions/observability/plugins/inventory/server/routes/get_global_inventory_route_repository.ts
index 598b69db90e5a..5a808ea9cc2cb 100644
--- a/x-pack/solutions/observability/plugins/inventory/server/routes/get_global_inventory_route_repository.ts
+++ b/x-pack/solutions/observability/plugins/inventory/server/routes/get_global_inventory_route_repository.ts
@@ -6,11 +6,13 @@
  */
 
 import { entitiesRoutes } from './entities/route';
+import { entityDefinitionsRoutes } from './entity_definition/get_entity_definitions';
 import { hasDataRoutes } from './has_data/route';
 
 export function getGlobalInventoryServerRouteRepository() {
   return {
     ...entitiesRoutes,
+    ...entityDefinitionsRoutes,
     ...hasDataRoutes,
   };
 }