Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[7.x] fix export response (#70473) #70531

Merged
merged 2 commits into from
Jul 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,15 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { shallow } from 'enzyme';
import { shallow, mount } from 'enzyme';
import React from 'react';
import { GenericDownloaderComponent } from './index';
import { GenericDownloaderComponent, ExportSelectedData } from './index';
import { errorToToaster } from '../toasters';

jest.mock('../toasters', () => ({
useStateToaster: jest.fn(() => [jest.fn(), jest.fn()]),
errorToToaster: jest.fn(),
}));

describe('GenericDownloader', () => {
test('renders correctly against snapshot', () => {
Expand All @@ -19,4 +25,16 @@ describe('GenericDownloader', () => {
);
expect(wrapper).toMatchSnapshot();
});

test('show toaster with correct error message if error occurrs', () => {
mount(
<GenericDownloaderComponent
filename={'export_rules.ndjson'}
onExportSuccess={jest.fn()}
exportSelectedData={('some error' as unknown) as ExportSelectedData}
ids={['123']}
/>
);
expect((errorToToaster as jest.Mock).mock.calls[0][0].title).toEqual('Failed to export data…');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
import { i18n } from '@kbn/i18n';

export const EXPORT_FAILURE = i18n.translate(
'xpack.securitySolution.detectionEngine.rules.components.ruleDownloader.exportFailureTitle',
'xpack.securitySolution.detectionEngine.rules.components.genericDownloader.exportFailureTitle',
{
defaultMessage: 'Failed to export rules…',
defaultMessage: 'Failed to export data…',
}
);
142 changes: 129 additions & 13 deletions x-pack/plugins/security_solution/public/timelines/containers/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import * as api from './api';
import { KibanaServices } from '../../common/lib/kibana';
import { TimelineType, TimelineStatus } from '../../../common/types/timeline';
import { TIMELINE_DRAFT_URL, TIMELINE_URL } from '../../../common/constants';
import { ImportDataProps } from '../../alerts/containers/detection_engine/rules/types';

jest.mock('../../common/lib/kibana', () => {
return {
KibanaServices: { get: jest.fn() },
KibanaServices: { get: jest.fn(() => ({ http: { fetch: jest.fn() } })) },
};
});

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

beforeAll(() => {
jest.resetAllMocks();
jest.resetModules();

(KibanaServices.get as jest.Mock).mockReturnValue({
http: {
Expand All @@ -188,10 +190,6 @@ describe('persistTimeline', () => {
});
});

afterAll(() => {
jest.resetAllMocks();
});

test('it should create a draft timeline if given status is draft and timelineId is null', () => {
expect(postMock).toHaveBeenCalledWith(TIMELINE_DRAFT_URL, {
body: JSON.stringify({
Expand Down Expand Up @@ -334,6 +332,7 @@ describe('persistTimeline', () => {

beforeAll(() => {
jest.resetAllMocks();
jest.resetModules();

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

afterAll(() => {
jest.resetAllMocks();
});

test('it should update timeline', () => {
expect(postMock.mock.calls[0][0]).toEqual(TIMELINE_URL);
});
Expand Down Expand Up @@ -474,6 +469,7 @@ describe('persistTimeline', () => {

beforeAll(() => {
jest.resetAllMocks();
jest.resetModules();

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

afterAll(() => {
jest.resetAllMocks();
});

test('it should update timeline', () => {
expect(patchMock.mock.calls[0][0]).toEqual(TIMELINE_URL);
});
Expand All @@ -506,3 +498,127 @@ describe('persistTimeline', () => {
});
});
});

describe('importTimelines', () => {
const fileToImport = { fileToImport: {} } as ImportDataProps;
const fetchMock = jest.fn();

beforeAll(() => {
jest.resetAllMocks();
jest.resetModules();

(KibanaServices.get as jest.Mock).mockReturnValue({
http: {
fetch: fetchMock,
},
});
api.importTimelines(fileToImport);
});

test('should pass correct args to KibanaServices - url', () => {
expect(fetchMock.mock.calls[0][0]).toEqual('/api/timeline/_import');
});

test('should pass correct args to KibanaServices - args', () => {
expect(JSON.stringify(fetchMock.mock.calls[0][1])).toEqual(
JSON.stringify({
method: 'POST',
headers: { 'Content-Type': undefined },
body: new FormData(),
signal: undefined,
})
);
});
});

describe('exportSelectedTimeline', () => {
const ids = ['123', 'abc'];
const fetchMock = jest.fn();

beforeAll(() => {
jest.resetAllMocks();
jest.resetModules();

(KibanaServices.get as jest.Mock).mockReturnValue({
http: {
fetch: fetchMock,
},
});
api.exportSelectedTimeline({
filename: 'timelines_export.ndjson',
ids,
signal: {} as AbortSignal,
});
});

test('should pass correct args to KibanaServices', () => {
expect(fetchMock).toBeCalledWith('/api/timeline/_export', {
body: JSON.stringify({ ids }),
method: 'POST',
query: { file_name: 'timelines_export.ndjson' },
signal: {},
});
});
});

describe('getDraftTimeline', () => {
const timelineType = { timelineType: TimelineType.default };
const getMock = jest.fn();

beforeAll(() => {
jest.resetAllMocks();
jest.resetModules();

(KibanaServices.get as jest.Mock).mockReturnValue({
http: {
get: getMock,
},
});
api.getDraftTimeline(timelineType);
});

test('should pass correct args to KibanaServices', () => {
expect(getMock).toBeCalledWith('/api/timeline/_draft', {
query: timelineType,
});
});
});

describe('cleanDraftTimeline', () => {
const postMock = jest.fn();

beforeEach(() => {
jest.resetAllMocks();
jest.resetModules();

(KibanaServices.get as jest.Mock).mockReturnValue({
http: {
post: postMock,
},
});
});

test('should pass correct args to KibanaServices - timeline', () => {
const args = { timelineType: TimelineType.default };

api.cleanDraftTimeline(args);

expect(postMock).toBeCalledWith('/api/timeline/_draft', {
body: JSON.stringify(args),
});
});

test('should pass correct args to KibanaServices - timeline template', () => {
const args = {
timelineType: TimelineType.template,
templateTimelineId: 'test-123',
templateTimelineVersion: 1,
};

api.cleanDraftTimeline(args);

expect(postMock).toBeCalledWith('/api/timeline/_draft', {
body: JSON.stringify(args),
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ export const persistTimeline = async ({

export const importTimelines = async ({
fileToImport,
signal,
}: ImportDataProps): Promise<ImportDataResponse> => {
const formData = new FormData();
formData.append('file', fileToImport);
Expand All @@ -140,24 +141,24 @@ export const importTimelines = async ({
method: 'POST',
headers: { 'Content-Type': undefined },
body: formData,
signal,
});
};

export const exportSelectedTimeline: ExportSelectedData = async ({
export const exportSelectedTimeline: ExportSelectedData = ({
filename = `timelines_export.ndjson`,
ids = [],
signal,
}): Promise<Blob> => {
const body = ids.length > 0 ? JSON.stringify({ ids }) : undefined;
const response = await KibanaServices.get().http.fetch<{ body: Blob }>(`${TIMELINE_EXPORT_URL}`, {
return KibanaServices.get().http.fetch<Blob>(`${TIMELINE_EXPORT_URL}`, {
method: 'POST',
body,
query: {
file_name: filename,
},
signal,
});

return response.body;
};

export const getDraftTimeline = async ({
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/translations/translations/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -14194,7 +14194,6 @@
"xpack.securitySolution.detectionEngine.rules.allRules.tabs.rules": "ルール",
"xpack.securitySolution.detectionEngine.rules.backOptionsHeader": "検出に戻る",
"xpack.securitySolution.detectionEngine.rules.components.ruleActionsOverflow.allActionsTitle": "すべてのアクション",
"xpack.securitySolution.detectionEngine.rules.components.ruleDownloader.exportFailureTitle": "ルールをエクスポートできませんでした...",
"xpack.securitySolution.detectionEngine.rules.continueButtonTitle": "続行",
"xpack.securitySolution.detectionEngine.rules.create.successfullyCreatedRuleTitle": "{ruleName}が作成されました",
"xpack.securitySolution.detectionEngine.rules.defineRuleTitle": "ルールの定義",
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/translations/translations/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -14200,7 +14200,6 @@
"xpack.securitySolution.detectionEngine.rules.allRules.tabs.rules": "规则",
"xpack.securitySolution.detectionEngine.rules.backOptionsHeader": "返回到检测",
"xpack.securitySolution.detectionEngine.rules.components.ruleActionsOverflow.allActionsTitle": "所有操作",
"xpack.securitySolution.detectionEngine.rules.components.ruleDownloader.exportFailureTitle": "无法导出规则……",
"xpack.securitySolution.detectionEngine.rules.continueButtonTitle": "继续",
"xpack.securitySolution.detectionEngine.rules.create.successfullyCreatedRuleTitle": "{ruleName} 已创建",
"xpack.securitySolution.detectionEngine.rules.defineRuleTitle": "定义规则",
Expand Down