diff --git a/x-pack/platform/plugins/shared/actions/server/application/connector/methods/get_all/get_all.test.ts b/x-pack/platform/plugins/shared/actions/server/application/connector/methods/get_all/get_all.test.ts index 479d15382a027..b79f913beae49 100644 --- a/x-pack/platform/plugins/shared/actions/server/application/connector/methods/get_all/get_all.test.ts +++ b/x-pack/platform/plugins/shared/actions/server/application/connector/methods/get_all/get_all.test.ts @@ -25,6 +25,7 @@ import type { Logger } from '@kbn/logging'; import { eventLogClientMock } from '@kbn/event-log-plugin/server/event_log_client.mock'; import type { ActionTypeRegistry } from '../../../../action_type_registry'; import { getAllUnsecured } from './get_all'; +import type { InferenceInferenceEndpointInfo } from '@elastic/elasticsearch/lib/api/types'; jest.mock('@kbn/core-saved-objects-utils-server', () => { const actual = jest.requireActual('@kbn/core-saved-objects-utils-server'); @@ -585,6 +586,84 @@ describe('getAll()', () => { expect(logger.warn).not.toHaveBeenCalled(); }); + + test('filters out inference connectors without endpoints', async () => { + unsecuredSavedObjectsClient.find.mockResolvedValueOnce({ + total: 1, + per_page: 10, + page: 1, + saved_objects: [], + }); + + scopedClusterClient.asInternalUser.search.mockResponse( + // @ts-expect-error not full search response + { + aggregations: { + testPreconfigured01: { doc_count: 2 }, + testPreconfigured02: { doc_count: 2 }, + }, + } + ); + + scopedClusterClient.asInternalUser.inference.get.mockResolvedValueOnce({ + endpoints: [{ inference_id: '2' } as InferenceInferenceEndpointInfo], + }); + + actionsClient = new ActionsClient({ + logger, + actionTypeRegistry, + unsecuredSavedObjectsClient, + scopedClusterClient, + kibanaIndices, + actionExecutor, + bulkExecutionEnqueuer, + request, + authorization: authorization as unknown as ActionsAuthorization, + inMemoryConnectors: [ + { + id: 'testPreconfigured01', + actionTypeId: '.inference', + name: 'test1', + config: { + inferenceId: '1', + }, + secrets: {}, + isDeprecated: false, + isMissingSecrets: false, + isPreconfigured: false, + isSystemAction: true, + }, + { + id: 'testPreconfigured02', + actionTypeId: '.inference', + name: 'test2', + config: { + inferenceId: '2', + }, + secrets: {}, + isDeprecated: false, + isMissingSecrets: false, + isPreconfigured: false, + isSystemAction: true, + }, + ], + connectorTokenClient: connectorTokenClientMock.create(), + getEventLogClient, + }); + + const result = await actionsClient.getAll({ includeSystemActions: true }); + expect(result).toEqual([ + { + actionTypeId: '.inference', + id: 'testPreconfigured02', + isDeprecated: false, + isPreconfigured: false, + isSystemAction: true, + name: 'test2', + referencedByCount: 2, + }, + ]); + }); }); describe('getAllSystemConnectors()', () => { diff --git a/x-pack/platform/plugins/shared/actions/server/application/connector/methods/get_all/get_all.ts b/x-pack/platform/plugins/shared/actions/server/application/connector/methods/get_all/get_all.ts index 6c0a0bf628759..6a07044bd2873 100644 --- a/x-pack/platform/plugins/shared/actions/server/application/connector/methods/get_all/get_all.ts +++ b/x-pack/platform/plugins/shared/actions/server/application/connector/methods/get_all/get_all.ts @@ -113,15 +113,17 @@ async function getAllHelper({ const mergedResult = [ ...savedObjectsActions, - ...inMemoryConnectors.map((inMemoryConnector) => ({ - id: inMemoryConnector.id, - actionTypeId: inMemoryConnector.actionTypeId, - name: inMemoryConnector.name, - isPreconfigured: inMemoryConnector.isPreconfigured, - isDeprecated: isConnectorDeprecated(inMemoryConnector), - isSystemAction: inMemoryConnector.isSystemAction, - ...(inMemoryConnector.exposeConfig ? { config: inMemoryConnector.config } : {}), - })), + ...(await filterInferenceConnectors(esClient, inMemoryConnectors)).map((connector) => { + return { + id: connector.id, + actionTypeId: connector.actionTypeId, + name: connector.name, + isPreconfigured: connector.isPreconfigured, + isDeprecated: isConnectorDeprecated(connector), + isSystemAction: connector.isSystemAction, + ...(connector.exposeConfig ? { config: connector.config } : {}), + }; + }), ].sort((a, b) => a.name.localeCompare(b.name)); const connectors = await injectExtraFindData({ @@ -238,3 +240,37 @@ async function injectExtraFindData({ referencedByCount: aggregationResult.aggregations[connector.id].doc_count, })); } + +/** + * Filters out inference connectors that do not have an endpoint. + * It requires a connector config in order to retrieve the inference id. + * + * @param esClient + * @param connectors + * @returns + */ +export async function filterInferenceConnectors( + esClient: ElasticsearchClient, + connectors: InMemoryConnector[] +): Promise { + let result = connectors; + + if (result.some((connector) => connector.actionTypeId === '.inference')) { + try { + // Get all inference endpoints to filter out inference connector without endpoints + const inferenceEndpoints = await esClient.inference.get(); + result = result.filter((connector) => { + if (connector.actionTypeId !== '.inference') return true; + + const inferenceEndpoint = inferenceEndpoints.endpoints.find( + (endpoint) => endpoint.inference_id === connector.config?.inferenceId + ); + return inferenceEndpoint !== undefined; + }); + } catch (e) { + // If we can't get the inference endpoints, we just return all connectors + } + } + + return result; +}