Skip to content

Commit 4cd0460

Browse files
[Cases ]Expose the bulk get cases API from the cases UI client (#154235)
## Summary This PR exposes the bulk get cases API from the cases UI client. Fixes: #153926 ### Checklist Delete any items that are not applicable to this PR. - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios ### For maintainers - [x] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --------- Co-authored-by: Kibana Machine <[email protected]>
1 parent 098c3fe commit 4cd0460

File tree

8 files changed

+117
-8
lines changed

8 files changed

+117
-8
lines changed

x-pack/plugins/cases/public/api/decoders.ts

+14
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,14 @@ import type {
1515
CasesFindResponse,
1616
CasesStatusResponse,
1717
CasesMetricsResponse,
18+
CasesBulkGetResponseCertainFields,
19+
CaseResponse,
1820
} from '../../common/api';
1921
import {
2022
CasesFindResponseRt,
2123
CasesStatusResponseRt,
24+
CasesResponseRt,
25+
getTypeForCertainFieldsFromArray,
2226
CasesMetricsResponseRt,
2327
} from '../../common/api';
2428

@@ -36,3 +40,13 @@ export const decodeCasesMetricsResponse = (metrics?: CasesMetricsResponse) =>
3640
CasesMetricsResponseRt.decode(metrics),
3741
fold(throwErrors(createToasterPlainError), identity)
3842
);
43+
44+
export const decodeCasesBulkGetResponse = <Field extends keyof CaseResponse = keyof CaseResponse>(
45+
res: CasesBulkGetResponseCertainFields<Field>,
46+
fields?: string[]
47+
) => {
48+
const typeToDecode = getTypeForCertainFieldsFromArray(CasesResponseRt, fields);
49+
pipe(typeToDecode.decode(res.cases), fold(throwErrors(createToasterPlainError), identity));
50+
51+
return res;
52+
};

x-pack/plugins/cases/public/api/index.test.ts

+29-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
*/
77

88
import { httpServiceMock } from '@kbn/core/public/mocks';
9-
import { getCases, getCasesMetrics } from '.';
10-
import { allCases, allCasesSnake } from '../containers/mock';
9+
import { bulkGetCases, getCases, getCasesMetrics } from '.';
10+
import { allCases, allCasesSnake, casesSnake } from '../containers/mock';
1111

1212
describe('api', () => {
1313
beforeEach(() => {
@@ -47,4 +47,31 @@ describe('api', () => {
4747
});
4848
});
4949
});
50+
51+
describe('bulkGetCases', () => {
52+
const http = httpServiceMock.createStartContract({ basePath: '' });
53+
http.post.mockResolvedValue({ cases: [{ title: 'test' }], errors: [] });
54+
55+
it('should return the correct cases with a subset of fields', async () => {
56+
expect(await bulkGetCases({ http, params: { ids: ['test'], fields: ['title'] } })).toEqual({
57+
cases: [{ title: 'test' }],
58+
errors: [],
59+
});
60+
});
61+
62+
it('should return the correct cases with all fields', async () => {
63+
http.post.mockResolvedValueOnce({ cases: casesSnake, errors: [] });
64+
expect(await bulkGetCases({ http, params: { ids: ['test'] } })).toEqual({
65+
cases: casesSnake,
66+
errors: [],
67+
});
68+
});
69+
70+
it('should have been called with the correct path', async () => {
71+
await bulkGetCases({ http, params: { ids: ['test'], fields: ['title'] } });
72+
expect(http.post).toHaveBeenCalledWith('/internal/cases/_bulk_get', {
73+
body: '{"ids":["test"],"fields":["title"]}',
74+
});
75+
});
76+
});
5077
});

x-pack/plugins/cases/public/api/index.ts

+28-1
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,16 @@
77

88
import type { HttpStart } from '@kbn/core/public';
99
import type { Cases, CasesStatus, CasesMetrics } from '../../common/ui';
10-
import { CASE_FIND_URL, CASE_METRICS_URL, CASE_STATUS_URL } from '../../common/constants';
10+
import {
11+
CASE_FIND_URL,
12+
CASE_METRICS_URL,
13+
CASE_STATUS_URL,
14+
INTERNAL_BULK_GET_CASES_URL,
15+
} from '../../common/constants';
1116
import type {
17+
CaseResponse,
18+
CasesBulkGetRequestCertainFields,
19+
CasesBulkGetResponseCertainFields,
1220
CasesFindRequest,
1321
CasesFindResponse,
1422
CasesMetricsRequest,
@@ -18,6 +26,7 @@ import type {
1826
} from '../../common/api';
1927
import { convertAllCasesToCamel, convertToCamelCase } from './utils';
2028
import {
29+
decodeCasesBulkGetResponse,
2130
decodeCasesFindResponse,
2231
decodeCasesMetricsResponse,
2332
decodeCasesStatusResponse,
@@ -58,3 +67,21 @@ export const getCasesMetrics = async ({
5867
const res = await http.get<CasesMetricsResponse>(CASE_METRICS_URL, { signal, query });
5968
return convertToCamelCase(decodeCasesMetricsResponse(res));
6069
};
70+
71+
export const bulkGetCases = async <Field extends keyof CaseResponse = keyof CaseResponse>({
72+
http,
73+
signal,
74+
params,
75+
}: HTTPService & { params: CasesBulkGetRequestCertainFields<Field> }): Promise<
76+
CasesBulkGetResponseCertainFields<Field>
77+
> => {
78+
const res = await http.post<CasesBulkGetResponseCertainFields<Field>>(
79+
INTERNAL_BULK_GET_CASES_URL,
80+
{
81+
body: JSON.stringify({ ...params }),
82+
signal,
83+
}
84+
);
85+
86+
return decodeCasesBulkGetResponse(res, params.fields);
87+
};

x-pack/plugins/cases/public/client/api/index.test.ts

+29-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
import { httpServiceMock } from '@kbn/core/public/mocks';
99
import { createClientAPI } from '.';
10-
import { allCases, allCasesSnake } from '../../containers/mock';
10+
import { allCases, allCasesSnake, casesSnake } from '../../containers/mock';
1111

1212
describe('createClientAPI', () => {
1313
beforeEach(() => {
@@ -80,5 +80,33 @@ describe('createClientAPI', () => {
8080
});
8181
});
8282
});
83+
84+
describe('bulkGet', () => {
85+
const http = httpServiceMock.createStartContract({ basePath: '' });
86+
const api = createClientAPI({ http });
87+
http.post.mockResolvedValue({ cases: [{ title: 'test' }], errors: [] });
88+
89+
it('should return the correct cases with a subset of fields', async () => {
90+
expect(await api.cases.bulkGet({ ids: ['test'], fields: ['title'] })).toEqual({
91+
cases: [{ title: 'test' }],
92+
errors: [],
93+
});
94+
});
95+
96+
it('should return the correct cases with all fields', async () => {
97+
http.post.mockResolvedValueOnce({ cases: casesSnake, errors: [] });
98+
expect(await api.cases.bulkGet({ ids: ['test'], fields: ['title'] })).toEqual({
99+
cases: casesSnake,
100+
errors: [],
101+
});
102+
});
103+
104+
it('should have been called with the correct path', async () => {
105+
await api.cases.bulkGet({ ids: ['test'], fields: ['title'] });
106+
expect(http.post).toHaveBeenCalledWith('/internal/cases/_bulk_get', {
107+
body: '{"ids":["test"],"fields":["title"]}',
108+
});
109+
});
110+
});
83111
});
84112
});

x-pack/plugins/cases/public/client/api/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import type {
1515
} from '../../../common/api';
1616
import { getCasesFromAlertsUrl } from '../../../common/api';
1717
import type { Cases, CasesStatus, CasesMetrics } from '../../../common/ui';
18-
import { getCases, getCasesMetrics, getCasesStatus } from '../../api';
18+
import { bulkGetCases, getCases, getCasesMetrics, getCasesStatus } from '../../api';
1919
import type { CasesUiStart } from '../../types';
2020

2121
export const createClientAPI = ({ http }: { http: HttpStart }): CasesUiStart['api'] => {
@@ -32,6 +32,7 @@ export const createClientAPI = ({ http }: { http: HttpStart }): CasesUiStart['ap
3232
getCasesStatus({ http, query, signal }),
3333
getCasesMetrics: (query: CasesMetricsRequest, signal?: AbortSignal): Promise<CasesMetrics> =>
3434
getCasesMetrics({ http, signal, query }),
35+
bulkGet: (params, signal?: AbortSignal) => bulkGetCases({ http, signal, params }),
3536
},
3637
};
3738
};

x-pack/plugins/cases/public/mocks.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,12 @@ import type { CasesUiStart } from './types';
1010

1111
const apiMock: jest.Mocked<CasesUiStart['api']> = {
1212
getRelatedCases: jest.fn(),
13-
cases: { find: jest.fn(), getCasesMetrics: jest.fn(), getCasesStatus: jest.fn() },
13+
cases: {
14+
find: jest.fn(),
15+
getCasesMetrics: jest.fn(),
16+
getCasesStatus: jest.fn(),
17+
bulkGet: jest.fn(),
18+
},
1419
};
1520

1621
const uiMock: jest.Mocked<CasesUiStart['ui']> = {

x-pack/plugins/cases/public/types.ts

+7
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ import type { LicensingPluginStart } from '@kbn/licensing-plugin/public';
2525
import type { FilesSetup, FilesStart } from '@kbn/files-plugin/public';
2626
import type { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public';
2727
import type {
28+
CaseResponse,
29+
CasesBulkGetRequestCertainFields,
30+
CasesBulkGetResponseCertainFields,
2831
CasesByAlertId,
2932
CasesByAlertIDRequest,
3033
CasesFindRequest,
@@ -102,6 +105,10 @@ export interface CasesUiStart {
102105
find: (query: CasesFindRequest, signal?: AbortSignal) => Promise<Cases>;
103106
getCasesStatus: (query: CasesStatusRequest, signal?: AbortSignal) => Promise<CasesStatus>;
104107
getCasesMetrics: (query: CasesMetricsRequest, signal?: AbortSignal) => Promise<CasesMetrics>;
108+
bulkGet: <Field extends keyof CaseResponse = keyof CaseResponse>(
109+
params: CasesBulkGetRequestCertainFields<Field>,
110+
signal?: AbortSignal
111+
) => Promise<CasesBulkGetResponseCertainFields<Field>>;
105112
};
106113
};
107114
ui: {

x-pack/plugins/cases/server/client/cases/client.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ export interface CasesSubClient {
6666
* Retrieves multiple cases with the specified IDs.
6767
*/
6868
bulkGet<Field extends keyof CaseResponse = keyof CaseResponse>(
69-
params: CasesBulkGetRequestCertainFields<Field | 'id' | 'version' | 'owner'>
70-
): Promise<CasesBulkGetResponseCertainFields<Field | 'id' | 'version' | 'owner'>>;
69+
params: CasesBulkGetRequestCertainFields<Field>
70+
): Promise<CasesBulkGetResponseCertainFields<Field>>;
7171
/**
7272
* Pushes a specific case to an external system.
7373
*/

0 commit comments

Comments
 (0)