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

[SIEM] Fix and consolidate handling of error responses in the client #59438

Merged
merged 32 commits into from
Mar 10, 2020
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
a540e68
Convert our manual throwing of TypeError to a custom Error
rylnd Mar 4, 2020
66c7120
Present API Error messages to the user
rylnd Mar 4, 2020
b71f90d
Remove unnecessary use of throwIfNotOk in our client API calls
rylnd Mar 4, 2020
5ab8824
Move errorToToaster and ToasterError to general location
rylnd Mar 4, 2020
964b148
Refactor Rules API functions to not use throwIfNotOk
rylnd Mar 4, 2020
c1bd216
Define a type for our BulkRule responses
rylnd Mar 5, 2020
15edf2d
Address case where bulk rules errors were not handled
rylnd Mar 5, 2020
797b607
Remove more throwIfNotOk uses from API calls
rylnd Mar 5, 2020
6421322
Display an error toaster on newsfeed fetch failure
rylnd Mar 5, 2020
cf80333
Remove dead code
rylnd Mar 5, 2020
69b90f8
Remove throwIfNotOk from case API calls
rylnd Mar 5, 2020
36c05f6
Update use_get_tags for NP
rylnd Mar 5, 2020
924508f
Remove throwIfNotOk from signals API
rylnd Mar 5, 2020
dcd8edf
Remove throwIfNotOk
rylnd Mar 5, 2020
d83a21c
Remove custom errors in favor of KibanaApiError and isApiError type p…
rylnd Mar 5, 2020
fd0e273
Fix test failures
rylnd Mar 5, 2020
5f7467a
Replace use of core mocks with our simpler local ones
rylnd Mar 5, 2020
71acc43
add signal api unit tests
XavierM Feb 21, 2020
4caf9ad
privilege unit test api
XavierM Feb 21, 2020
252c77c
Add unit tests on the signals container
XavierM Feb 25, 2020
780bbaf
Refactor signals API tests to use core mocks
rylnd Feb 27, 2020
063d60a
Simplify signals API tests now that the subjects do less
rylnd Mar 5, 2020
f3477df
Merge branch 'master' into fix_siem_errors
rylnd Mar 5, 2020
9ca2c16
Merge branch 'master' into fix_siem_errors
rylnd Mar 6, 2020
ba40135
Merge branch 'master' into fix_siem_errors
rylnd Mar 9, 2020
9e36715
Simplify API functions to use implict returns
rylnd Mar 9, 2020
177490e
Revert "Display an error toaster on newsfeed fetch failure"
rylnd Mar 9, 2020
22a6cd4
Error property is readonly
rylnd Mar 10, 2020
793aeec
Pull uuid generation into default argument value
rylnd Mar 10, 2020
a840667
Fix type predicate isApiError
rylnd Mar 10, 2020
6ecc237
Fix test setup following modification of type predicate
rylnd Mar 10, 2020
e28734d
Merge branch 'master' into fix_siem_errors
elasticmachine Mar 10, 2020
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 @@ -10,8 +10,7 @@ import { InfluencerInput, Anomalies, CriteriaFields } from '../types';
import { hasMlUserPermissions } from '../permissions/has_ml_user_permissions';
import { MlCapabilitiesContext } from '../permissions/ml_capabilities_provider';
import { useSiemJobs } from '../../ml_popover/hooks/use_siem_jobs';
import { useStateToaster } from '../../toasters';
import { errorToToaster } from '../api/error_to_toaster';
import { useStateToaster, errorToToaster } from '../../toasters';

import * as i18n from './translations';
import { useTimeZone, useUiSetting$ } from '../../../lib/kibana';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
*/

import { Anomalies, InfluencerInput, CriteriaFields } from '../types';
import { throwIfNotOk } from '../../../hooks/api/api';
import { KibanaServices } from '../../../lib/kibana';

export interface Body {
Expand All @@ -22,17 +21,10 @@ export interface Body {
}

export const anomaliesTableData = async (body: Body, signal: AbortSignal): Promise<Anomalies> => {
const response = await KibanaServices.get().http.fetch<Anomalies>(
'/api/ml/results/anomalies_table_data',
{
method: 'POST',
body: JSON.stringify(body),
asResponse: true,
asSystemRequest: true,
signal,
}
);

await throwIfNotOk(response.response);
return response.body!;
return KibanaServices.get().http.fetch<Anomalies>('/api/ml/results/anomalies_table_data', {
method: 'POST',
body: JSON.stringify(body),
asSystemRequest: true,
signal,
});
};

This file was deleted.

20 changes: 20 additions & 0 deletions x-pack/legacy/plugins/siem/public/components/ml/api/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { has } from 'lodash/fp';

import { MlError } from '../types';

export interface MlStartJobError {
error: MlError;
started: boolean;
}

// use the "in operator" and regular type guards to do a narrow once this issue is fixed below:
// https://github.com/microsoft/TypeScript/issues/21732
// Otherwise for now, has will work ok even though it casts 'unknown' to 'any'
export const isMlStartJobError = (value: unknown): value is MlStartJobError =>
has('error.msg', value) && has('error.response', value) && has('error.statusCode', value);
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
*/

import { InfluencerInput, MlCapabilities } from '../types';
import { throwIfNotOk } from '../../../hooks/api/api';
import { KibanaServices } from '../../../lib/kibana';

export interface Body {
Expand All @@ -22,16 +21,9 @@ export interface Body {
}

export const getMlCapabilities = async (signal: AbortSignal): Promise<MlCapabilities> => {
const response = await KibanaServices.get().http.fetch<MlCapabilities>(
'/api/ml/ml_capabilities',
{
method: 'GET',
asResponse: true,
asSystemRequest: true,
signal,
}
);

await throwIfNotOk(response.response);
return response.body!;
return KibanaServices.get().http.fetch<MlCapabilities>('/api/ml/ml_capabilities', {
method: 'GET',
asSystemRequest: true,
signal,
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,48 +5,21 @@
*/

import fetchMock from 'fetch-mock';

import { ToasterError } from '../../toasters';
import { SetupMlResponse } from '../../ml_popover/types';
import { isMlStartJobError } from './errors';
import {
isMlStartJobError,
MessageBody,
parseJsonFromBody,
throwIfErrorAttached,
throwIfErrorAttachedToSetup,
ToasterErrors,
tryParseResponse,
} from './throw_if_not_ok';
import { SetupMlResponse } from '../../ml_popover/types';

describe('throw_if_not_ok', () => {
afterEach(() => {
fetchMock.reset();
});

describe('#parseJsonFromBody', () => {
test('parses a json from the body correctly', async () => {
fetchMock.mock('http://example.com', {
status: 500,
body: {
error: 'some error',
statusCode: 500,
message: 'I am a custom message',
},
});
const response = await fetch('http://example.com');
const expected: MessageBody = {
error: 'some error',
statusCode: 500,
message: 'I am a custom message',
};
await expect(parseJsonFromBody(response)).resolves.toEqual(expected);
});

test('returns null if the body does not exist', async () => {
fetchMock.mock('http://example.com', { status: 500, body: 'some text' });
const response = await fetch('http://example.com');
await expect(parseJsonFromBody(response)).resolves.toEqual(null);
});
});

describe('#tryParseResponse', () => {
test('It formats a JSON object', () => {
const parsed = tryParseResponse(JSON.stringify({ hello: 'how are you?' }));
Expand Down Expand Up @@ -119,7 +92,7 @@ describe('throw_if_not_ok', () => {
},
};
expect(() => throwIfErrorAttached(json, ['some-id'])).toThrow(
new ToasterErrors(['some message'])
new ToasterError(['some message'])
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { has } from 'lodash/fp';

import * as i18n from './translations';
import { MlError } from '../types';
import { ToasterError } from '../../toasters';
import { SetupMlResponse } from '../../ml_popover/types';

export { MessageBody, parseJsonFromBody } from '../../../utils/api';

export interface MlStartJobError {
error: MlError;
started: boolean;
}

export type ToasterErrorsType = Error & {
messages: string[];
};

export class ToasterErrors extends Error implements ToasterErrorsType {
public messages: string[];

constructor(messages: string[]) {
super(messages[0]);
this.name = 'ToasterErrors';
this.messages = messages;
}
}
import { isMlStartJobError } from './errors';

export const tryParseResponse = (response: string): string => {
try {
Expand Down Expand Up @@ -71,7 +49,7 @@ export const throwIfErrorAttachedToSetup = (

const errors = [...jobErrors, ...dataFeedErrors];
if (errors.length > 0) {
throw new ToasterErrors(errors);
throw new ToasterError(errors);
}
};

Expand All @@ -93,12 +71,6 @@ export const throwIfErrorAttached = (
}
}, []);
if (errors.length > 0) {
throw new ToasterErrors(errors);
throw new ToasterError(errors);
}
};

// use the "in operator" and regular type guards to do a narrow once this issue is fixed below:
// https://github.com/microsoft/TypeScript/issues/21732
// Otherwise for now, has will work ok even though it casts 'unknown' to 'any'
export const isMlStartJobError = (value: unknown): value is MlStartJobError =>
has('error.msg', value) && has('error.response', value) && has('error.statusCode', value);
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ import React, { useState, useEffect } from 'react';
import { MlCapabilities } from '../types';
import { getMlCapabilities } from '../api/get_ml_capabilities';
import { emptyMlCapabilities } from '../empty_ml_capabilities';
import { errorToToaster } from '../api/error_to_toaster';
import { useStateToaster } from '../../toasters';
import { errorToToaster, useStateToaster } from '../../toasters';

import * as i18n from './translations';

Expand Down
Loading