diff --git a/x-pack/plugins/cases/public/api/decoders.ts b/x-pack/plugins/cases/public/api/decoders.ts index 1022e0583f8dc..6402b2d56a342 100644 --- a/x-pack/plugins/cases/public/api/decoders.ts +++ b/x-pack/plugins/cases/public/api/decoders.ts @@ -15,10 +15,14 @@ import type { CasesFindResponse, CasesStatusResponse, CasesMetricsResponse, + CasesBulkGetResponseCertainFields, + CaseResponse, } from '../../common/api'; import { CasesFindResponseRt, CasesStatusResponseRt, + CasesResponseRt, + getTypeForCertainFieldsFromArray, CasesMetricsResponseRt, } from '../../common/api'; @@ -36,3 +40,13 @@ export const decodeCasesMetricsResponse = (metrics?: CasesMetricsResponse) => CasesMetricsResponseRt.decode(metrics), fold(throwErrors(createToasterPlainError), identity) ); + +export const decodeCasesBulkGetResponse = ( + res: CasesBulkGetResponseCertainFields, + fields?: string[] +) => { + const typeToDecode = getTypeForCertainFieldsFromArray(CasesResponseRt, fields); + pipe(typeToDecode.decode(res.cases), fold(throwErrors(createToasterPlainError), identity)); + + return res; +}; diff --git a/x-pack/plugins/cases/public/api/index.test.ts b/x-pack/plugins/cases/public/api/index.test.ts index 321a1db206846..c64a204183bed 100644 --- a/x-pack/plugins/cases/public/api/index.test.ts +++ b/x-pack/plugins/cases/public/api/index.test.ts @@ -6,8 +6,8 @@ */ import { httpServiceMock } from '@kbn/core/public/mocks'; -import { getCases, getCasesMetrics } from '.'; -import { allCases, allCasesSnake } from '../containers/mock'; +import { bulkGetCases, getCases, getCasesMetrics } from '.'; +import { allCases, allCasesSnake, casesSnake } from '../containers/mock'; describe('api', () => { beforeEach(() => { @@ -47,4 +47,31 @@ describe('api', () => { }); }); }); + + describe('bulkGetCases', () => { + const http = httpServiceMock.createStartContract({ basePath: '' }); + http.post.mockResolvedValue({ cases: [{ title: 'test' }], errors: [] }); + + it('should return the correct cases with a subset of fields', async () => { + expect(await bulkGetCases({ http, params: { ids: ['test'], fields: ['title'] } })).toEqual({ + cases: [{ title: 'test' }], + errors: [], + }); + }); + + it('should return the correct cases with all fields', async () => { + http.post.mockResolvedValueOnce({ cases: casesSnake, errors: [] }); + expect(await bulkGetCases({ http, params: { ids: ['test'] } })).toEqual({ + cases: casesSnake, + errors: [], + }); + }); + + it('should have been called with the correct path', async () => { + await bulkGetCases({ http, params: { ids: ['test'], fields: ['title'] } }); + expect(http.post).toHaveBeenCalledWith('/internal/cases/_bulk_get', { + body: '{"ids":["test"],"fields":["title"]}', + }); + }); + }); }); diff --git a/x-pack/plugins/cases/public/api/index.ts b/x-pack/plugins/cases/public/api/index.ts index 47cc0b6f108a8..c89b1ff94f9e9 100644 --- a/x-pack/plugins/cases/public/api/index.ts +++ b/x-pack/plugins/cases/public/api/index.ts @@ -7,8 +7,16 @@ import type { HttpStart } from '@kbn/core/public'; import type { Cases, CasesStatus, CasesMetrics } from '../../common/ui'; -import { CASE_FIND_URL, CASE_METRICS_URL, CASE_STATUS_URL } from '../../common/constants'; +import { + CASE_FIND_URL, + CASE_METRICS_URL, + CASE_STATUS_URL, + INTERNAL_BULK_GET_CASES_URL, +} from '../../common/constants'; import type { + CaseResponse, + CasesBulkGetRequestCertainFields, + CasesBulkGetResponseCertainFields, CasesFindRequest, CasesFindResponse, CasesMetricsRequest, @@ -18,6 +26,7 @@ import type { } from '../../common/api'; import { convertAllCasesToCamel, convertToCamelCase } from './utils'; import { + decodeCasesBulkGetResponse, decodeCasesFindResponse, decodeCasesMetricsResponse, decodeCasesStatusResponse, @@ -58,3 +67,21 @@ export const getCasesMetrics = async ({ const res = await http.get(CASE_METRICS_URL, { signal, query }); return convertToCamelCase(decodeCasesMetricsResponse(res)); }; + +export const bulkGetCases = async ({ + http, + signal, + params, +}: HTTPService & { params: CasesBulkGetRequestCertainFields }): Promise< + CasesBulkGetResponseCertainFields +> => { + const res = await http.post>( + INTERNAL_BULK_GET_CASES_URL, + { + body: JSON.stringify({ ...params }), + signal, + } + ); + + return decodeCasesBulkGetResponse(res, params.fields); +}; diff --git a/x-pack/plugins/cases/public/client/api/index.test.ts b/x-pack/plugins/cases/public/client/api/index.test.ts index dacea3350bd4a..f53de5eb20f18 100644 --- a/x-pack/plugins/cases/public/client/api/index.test.ts +++ b/x-pack/plugins/cases/public/client/api/index.test.ts @@ -7,7 +7,7 @@ import { httpServiceMock } from '@kbn/core/public/mocks'; import { createClientAPI } from '.'; -import { allCases, allCasesSnake } from '../../containers/mock'; +import { allCases, allCasesSnake, casesSnake } from '../../containers/mock'; describe('createClientAPI', () => { beforeEach(() => { @@ -80,5 +80,33 @@ describe('createClientAPI', () => { }); }); }); + + describe('bulkGet', () => { + const http = httpServiceMock.createStartContract({ basePath: '' }); + const api = createClientAPI({ http }); + http.post.mockResolvedValue({ cases: [{ title: 'test' }], errors: [] }); + + it('should return the correct cases with a subset of fields', async () => { + expect(await api.cases.bulkGet({ ids: ['test'], fields: ['title'] })).toEqual({ + cases: [{ title: 'test' }], + errors: [], + }); + }); + + it('should return the correct cases with all fields', async () => { + http.post.mockResolvedValueOnce({ cases: casesSnake, errors: [] }); + expect(await api.cases.bulkGet({ ids: ['test'], fields: ['title'] })).toEqual({ + cases: casesSnake, + errors: [], + }); + }); + + it('should have been called with the correct path', async () => { + await api.cases.bulkGet({ ids: ['test'], fields: ['title'] }); + expect(http.post).toHaveBeenCalledWith('/internal/cases/_bulk_get', { + body: '{"ids":["test"],"fields":["title"]}', + }); + }); + }); }); }); diff --git a/x-pack/plugins/cases/public/client/api/index.ts b/x-pack/plugins/cases/public/client/api/index.ts index 6d902940c2200..09a172121d08c 100644 --- a/x-pack/plugins/cases/public/client/api/index.ts +++ b/x-pack/plugins/cases/public/client/api/index.ts @@ -15,7 +15,7 @@ import type { } from '../../../common/api'; import { getCasesFromAlertsUrl } from '../../../common/api'; import type { Cases, CasesStatus, CasesMetrics } from '../../../common/ui'; -import { getCases, getCasesMetrics, getCasesStatus } from '../../api'; +import { bulkGetCases, getCases, getCasesMetrics, getCasesStatus } from '../../api'; import type { CasesUiStart } from '../../types'; export const createClientAPI = ({ http }: { http: HttpStart }): CasesUiStart['api'] => { @@ -32,6 +32,7 @@ export const createClientAPI = ({ http }: { http: HttpStart }): CasesUiStart['ap getCasesStatus({ http, query, signal }), getCasesMetrics: (query: CasesMetricsRequest, signal?: AbortSignal): Promise => getCasesMetrics({ http, signal, query }), + bulkGet: (params, signal?: AbortSignal) => bulkGetCases({ http, signal, params }), }, }; }; diff --git a/x-pack/plugins/cases/public/mocks.ts b/x-pack/plugins/cases/public/mocks.ts index b16e4895a6463..2c4a653254195 100644 --- a/x-pack/plugins/cases/public/mocks.ts +++ b/x-pack/plugins/cases/public/mocks.ts @@ -10,7 +10,12 @@ import type { CasesUiStart } from './types'; const apiMock: jest.Mocked = { getRelatedCases: jest.fn(), - cases: { find: jest.fn(), getCasesMetrics: jest.fn(), getCasesStatus: jest.fn() }, + cases: { + find: jest.fn(), + getCasesMetrics: jest.fn(), + getCasesStatus: jest.fn(), + bulkGet: jest.fn(), + }, }; const uiMock: jest.Mocked = { diff --git a/x-pack/plugins/cases/public/types.ts b/x-pack/plugins/cases/public/types.ts index 7df2e8f950271..e2cb59095f849 100644 --- a/x-pack/plugins/cases/public/types.ts +++ b/x-pack/plugins/cases/public/types.ts @@ -25,6 +25,9 @@ import type { LicensingPluginStart } from '@kbn/licensing-plugin/public'; import type { FilesSetup, FilesStart } from '@kbn/files-plugin/public'; import type { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; import type { + CaseResponse, + CasesBulkGetRequestCertainFields, + CasesBulkGetResponseCertainFields, CasesByAlertId, CasesByAlertIDRequest, CasesFindRequest, @@ -102,6 +105,10 @@ export interface CasesUiStart { find: (query: CasesFindRequest, signal?: AbortSignal) => Promise; getCasesStatus: (query: CasesStatusRequest, signal?: AbortSignal) => Promise; getCasesMetrics: (query: CasesMetricsRequest, signal?: AbortSignal) => Promise; + bulkGet: ( + params: CasesBulkGetRequestCertainFields, + signal?: AbortSignal + ) => Promise>; }; }; ui: { diff --git a/x-pack/plugins/cases/server/client/cases/client.ts b/x-pack/plugins/cases/server/client/cases/client.ts index 28f4662b1bc6d..bf3253fda6558 100644 --- a/x-pack/plugins/cases/server/client/cases/client.ts +++ b/x-pack/plugins/cases/server/client/cases/client.ts @@ -66,8 +66,8 @@ export interface CasesSubClient { * Retrieves multiple cases with the specified IDs. */ bulkGet( - params: CasesBulkGetRequestCertainFields - ): Promise>; + params: CasesBulkGetRequestCertainFields + ): Promise>; /** * Pushes a specific case to an external system. */