Skip to content

Commit a145421

Browse files
committed
fix export response (#70473)
* fix export response * update unit tests
1 parent c2ebad5 commit a145421

4 files changed

Lines changed: 156 additions & 21 deletions

File tree

x-pack/plugins/security_solution/public/common/components/generic_downloader/index.test.tsx

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,15 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66

7-
import { shallow } from 'enzyme';
7+
import { shallow, mount } from 'enzyme';
88
import React from 'react';
9-
import { GenericDownloaderComponent } from './index';
9+
import { GenericDownloaderComponent, ExportSelectedData } from './index';
10+
import { errorToToaster } from '../toasters';
11+
12+
jest.mock('../toasters', () => ({
13+
useStateToaster: jest.fn(() => [jest.fn(), jest.fn()]),
14+
errorToToaster: jest.fn(),
15+
}));
1016

1117
describe('GenericDownloader', () => {
1218
test('renders correctly against snapshot', () => {
@@ -19,4 +25,16 @@ describe('GenericDownloader', () => {
1925
);
2026
expect(wrapper).toMatchSnapshot();
2127
});
28+
29+
test('show toaster with correct error message if error occurrs', () => {
30+
mount(
31+
<GenericDownloaderComponent
32+
filename={'export_rules.ndjson'}
33+
onExportSuccess={jest.fn()}
34+
exportSelectedData={('some error' as unknown) as ExportSelectedData}
35+
ids={['123']}
36+
/>
37+
);
38+
expect((errorToToaster as jest.Mock).mock.calls[0][0].title).toEqual('Failed to export data…');
39+
});
2240
});

x-pack/plugins/security_solution/public/common/components/generic_downloader/translations.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
import { i18n } from '@kbn/i18n';
88

99
export const EXPORT_FAILURE = i18n.translate(
10-
'xpack.securitySolution.detectionEngine.rules.components.ruleDownloader.exportFailureTitle',
10+
'xpack.securitySolution.detectionEngine.rules.components.genericDownloader.exportFailureTitle',
1111
{
12-
defaultMessage: 'Failed to export rules…',
12+
defaultMessage: 'Failed to export data…',
1313
}
1414
);

x-pack/plugins/security_solution/public/timelines/containers/api.test.ts

Lines changed: 129 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ import * as api from './api';
77
import { KibanaServices } from '../../common/lib/kibana';
88
import { TimelineType, TimelineStatus } from '../../../common/types/timeline';
99
import { TIMELINE_DRAFT_URL, TIMELINE_URL } from '../../../common/constants';
10+
import { ImportDataProps } from '../../alerts/containers/detection_engine/rules/types';
1011

1112
jest.mock('../../common/lib/kibana', () => {
1213
return {
13-
KibanaServices: { get: jest.fn() },
14+
KibanaServices: { get: jest.fn(() => ({ http: { fetch: jest.fn() } })) },
1415
};
1516
});
1617

@@ -173,6 +174,7 @@ describe('persistTimeline', () => {
173174

174175
beforeAll(() => {
175176
jest.resetAllMocks();
177+
jest.resetModules();
176178

177179
(KibanaServices.get as jest.Mock).mockReturnValue({
178180
http: {
@@ -188,10 +190,6 @@ describe('persistTimeline', () => {
188190
});
189191
});
190192

191-
afterAll(() => {
192-
jest.resetAllMocks();
193-
});
194-
195193
test('it should create a draft timeline if given status is draft and timelineId is null', () => {
196194
expect(postMock).toHaveBeenCalledWith(TIMELINE_DRAFT_URL, {
197195
body: JSON.stringify({
@@ -334,6 +332,7 @@ describe('persistTimeline', () => {
334332

335333
beforeAll(() => {
336334
jest.resetAllMocks();
335+
jest.resetModules();
337336

338337
(KibanaServices.get as jest.Mock).mockReturnValue({
339338
http: {
@@ -345,10 +344,6 @@ describe('persistTimeline', () => {
345344
api.persistTimeline({ timelineId, timeline: importTimeline, version });
346345
});
347346

348-
afterAll(() => {
349-
jest.resetAllMocks();
350-
});
351-
352347
test('it should update timeline', () => {
353348
expect(postMock.mock.calls[0][0]).toEqual(TIMELINE_URL);
354349
});
@@ -474,6 +469,7 @@ describe('persistTimeline', () => {
474469

475470
beforeAll(() => {
476471
jest.resetAllMocks();
472+
jest.resetModules();
477473

478474
(KibanaServices.get as jest.Mock).mockReturnValue({
479475
http: {
@@ -485,10 +481,6 @@ describe('persistTimeline', () => {
485481
api.persistTimeline({ timelineId, timeline: inputTimeline, version });
486482
});
487483

488-
afterAll(() => {
489-
jest.resetAllMocks();
490-
});
491-
492484
test('it should update timeline', () => {
493485
expect(patchMock.mock.calls[0][0]).toEqual(TIMELINE_URL);
494486
});
@@ -506,3 +498,127 @@ describe('persistTimeline', () => {
506498
});
507499
});
508500
});
501+
502+
describe('importTimelines', () => {
503+
const fileToImport = { fileToImport: {} } as ImportDataProps;
504+
const fetchMock = jest.fn();
505+
506+
beforeAll(() => {
507+
jest.resetAllMocks();
508+
jest.resetModules();
509+
510+
(KibanaServices.get as jest.Mock).mockReturnValue({
511+
http: {
512+
fetch: fetchMock,
513+
},
514+
});
515+
api.importTimelines(fileToImport);
516+
});
517+
518+
test('should pass correct args to KibanaServices - url', () => {
519+
expect(fetchMock.mock.calls[0][0]).toEqual('/api/timeline/_import');
520+
});
521+
522+
test('should pass correct args to KibanaServices - args', () => {
523+
expect(JSON.stringify(fetchMock.mock.calls[0][1])).toEqual(
524+
JSON.stringify({
525+
method: 'POST',
526+
headers: { 'Content-Type': undefined },
527+
body: new FormData(),
528+
signal: undefined,
529+
})
530+
);
531+
});
532+
});
533+
534+
describe('exportSelectedTimeline', () => {
535+
const ids = ['123', 'abc'];
536+
const fetchMock = jest.fn();
537+
538+
beforeAll(() => {
539+
jest.resetAllMocks();
540+
jest.resetModules();
541+
542+
(KibanaServices.get as jest.Mock).mockReturnValue({
543+
http: {
544+
fetch: fetchMock,
545+
},
546+
});
547+
api.exportSelectedTimeline({
548+
filename: 'timelines_export.ndjson',
549+
ids,
550+
signal: {} as AbortSignal,
551+
});
552+
});
553+
554+
test('should pass correct args to KibanaServices', () => {
555+
expect(fetchMock).toBeCalledWith('/api/timeline/_export', {
556+
body: JSON.stringify({ ids }),
557+
method: 'POST',
558+
query: { file_name: 'timelines_export.ndjson' },
559+
signal: {},
560+
});
561+
});
562+
});
563+
564+
describe('getDraftTimeline', () => {
565+
const timelineType = { timelineType: TimelineType.default };
566+
const getMock = jest.fn();
567+
568+
beforeAll(() => {
569+
jest.resetAllMocks();
570+
jest.resetModules();
571+
572+
(KibanaServices.get as jest.Mock).mockReturnValue({
573+
http: {
574+
get: getMock,
575+
},
576+
});
577+
api.getDraftTimeline(timelineType);
578+
});
579+
580+
test('should pass correct args to KibanaServices', () => {
581+
expect(getMock).toBeCalledWith('/api/timeline/_draft', {
582+
query: timelineType,
583+
});
584+
});
585+
});
586+
587+
describe('cleanDraftTimeline', () => {
588+
const postMock = jest.fn();
589+
590+
beforeEach(() => {
591+
jest.resetAllMocks();
592+
jest.resetModules();
593+
594+
(KibanaServices.get as jest.Mock).mockReturnValue({
595+
http: {
596+
post: postMock,
597+
},
598+
});
599+
});
600+
601+
test('should pass correct args to KibanaServices - timeline', () => {
602+
const args = { timelineType: TimelineType.default };
603+
604+
api.cleanDraftTimeline(args);
605+
606+
expect(postMock).toBeCalledWith('/api/timeline/_draft', {
607+
body: JSON.stringify(args),
608+
});
609+
});
610+
611+
test('should pass correct args to KibanaServices - timeline template', () => {
612+
const args = {
613+
timelineType: TimelineType.template,
614+
templateTimelineId: 'test-123',
615+
templateTimelineVersion: 1,
616+
};
617+
618+
api.cleanDraftTimeline(args);
619+
620+
expect(postMock).toBeCalledWith('/api/timeline/_draft', {
621+
body: JSON.stringify(args),
622+
});
623+
});
624+
});

x-pack/plugins/security_solution/public/timelines/containers/api.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ export const persistTimeline = async ({
132132

133133
export const importTimelines = async ({
134134
fileToImport,
135+
signal,
135136
}: ImportDataProps): Promise<ImportDataResponse> => {
136137
const formData = new FormData();
137138
formData.append('file', fileToImport);
@@ -140,24 +141,24 @@ export const importTimelines = async ({
140141
method: 'POST',
141142
headers: { 'Content-Type': undefined },
142143
body: formData,
144+
signal,
143145
});
144146
};
145147

146-
export const exportSelectedTimeline: ExportSelectedData = async ({
148+
export const exportSelectedTimeline: ExportSelectedData = ({
147149
filename = `timelines_export.ndjson`,
148150
ids = [],
149151
signal,
150152
}): Promise<Blob> => {
151153
const body = ids.length > 0 ? JSON.stringify({ ids }) : undefined;
152-
const response = await KibanaServices.get().http.fetch<{ body: Blob }>(`${TIMELINE_EXPORT_URL}`, {
154+
return KibanaServices.get().http.fetch<Blob>(`${TIMELINE_EXPORT_URL}`, {
153155
method: 'POST',
154156
body,
155157
query: {
156158
file_name: filename,
157159
},
160+
signal,
158161
});
159-
160-
return response.body;
161162
};
162163

163164
export const getDraftTimeline = async ({

0 commit comments

Comments
 (0)