diff --git a/public/pages/Main/Main.tsx b/public/pages/Main/Main.tsx index e3a548dc..f37080b6 100644 --- a/public/pages/Main/Main.tsx +++ b/public/pages/Main/Main.tsx @@ -275,6 +275,7 @@ export default class Main extends Component { this.setState({ selectedDataSource: { ...sources[0] }, }); + DataStore.logTypes.getLogTypes(); } dataSourceObservable.next({ id: this.state.selectedDataSource.id, diff --git a/public/plugin.ts b/public/plugin.ts index e40a2949..ba5f7056 100644 --- a/public/plugin.ts +++ b/public/plugin.ts @@ -53,7 +53,7 @@ import { BehaviorSubject } from 'rxjs'; export interface SecurityAnalyticsPluginSetupDeps { data: DataPublicPluginSetup; - dataSourceManagement: DataSourceManagementPluginSetup; + dataSourceManagement?: DataSourceManagementPluginSetup; } export interface SecurityAnalyticsPluginStartDeps { data: DataPublicPluginStart; @@ -232,7 +232,13 @@ export class SecurityAnalyticsPlugin { id: THREAT_ALERTS_NAV_ID, showInAllNavGroup: true }, { id: FINDINGS_NAV_ID, showInAllNavGroup: true }, { id: CORRELATIONS_NAV_ID, showInAllNavGroup: true }, - { id: PLUGIN_NAME, category: DEFAULT_APP_CATEGORIES.configure, title: 'Threat detection', showInAllNavGroup: true, order: 600 }, + { + id: PLUGIN_NAME, + category: DEFAULT_APP_CATEGORIES.configure, + title: 'Threat detection', + showInAllNavGroup: true, + order: 600, + }, { id: DETECTORS_NAV_ID, parentNavLinkId: PLUGIN_NAME, showInAllNavGroup: true }, { id: DETECTION_RULE_NAV_ID, parentNavLinkId: PLUGIN_NAME, showInAllNavGroup: true }, { id: CORRELATIONS_RULE_NAV_ID, showInAllNavGroup: true }, diff --git a/public/security_analytics_app.tsx b/public/security_analytics_app.tsx index b1723e6d..a084f5b8 100644 --- a/public/security_analytics_app.tsx +++ b/public/security_analytics_app.tsx @@ -35,81 +35,86 @@ export function renderApp( const services = getBrowserServices(); const metrics = new MetricsContext(services.metricsService); - DataStore.logTypes.getLogTypes().then((logTypes: LogType[]) => { - ReactDOM.render( - - { - const originalMethods = { - push: props.history.push, - replace: props.history.replace, - }; - const wrapper = (method: 'push' | 'replace') => ( - ...args: Parameters - ) => { - if (typeof args[0] === 'string') { - const url = new URL(args[0], window.location.origin); - const searchParams = url.searchParams; - searchParams.set('dataSourceId', dataSourceInfo.activeDataSource.id); - originalMethods[method]( - { - pathname: url.pathname, - search: searchParams.toString(), - }, - ...args.slice(1) - ); - } else if (typeof args[0] === 'object') { - const searchParams = new URLSearchParams(args[0].search); - searchParams.set('dataSourceId', dataSourceInfo.activeDataSource.id); - originalMethods[method]( - { - ...args[0], - search: searchParams.toString(), - }, - ...args.slice(1) - ); - } else { - originalMethods[method](...args); - } - }; + DataStore.logTypes + .getLogTypes() + .then((logTypes: LogType[]) => { + ReactDOM.render( + + { + const originalMethods = { + push: props.history.push, + replace: props.history.replace, + }; + const wrapper = (method: 'push' | 'replace') => ( + ...args: Parameters + ) => { + if (typeof args[0] === 'string') { + const url = new URL(args[0], window.location.origin); + const searchParams = url.searchParams; + searchParams.set('dataSourceId', dataSourceInfo.activeDataSource.id); + originalMethods[method]( + { + pathname: url.pathname, + search: searchParams.toString(), + }, + ...args.slice(1) + ); + } else if (typeof args[0] === 'object') { + const searchParams = new URLSearchParams(args[0].search); + searchParams.set('dataSourceId', dataSourceInfo.activeDataSource.id); + originalMethods[method]( + { + ...args[0], + search: searchParams.toString(), + }, + ...args.slice(1) + ); + } else { + originalMethods[method](...args); + } + }; - props.history = { - ...props.history, - push: wrapper('push'), - replace: wrapper('replace'), - }; + props.history = { + ...props.history, + push: wrapper('push'), + replace: wrapper('replace'), + }; - return ( - - - -
- - - - ); - }} - /> - , - params.element - ); + return ( + + + +
+ + + + ); + }} + /> + , + params.element + ); - services.notificationsService.getServerFeatures().then((response) => { - if (response.ok) { - CHANNEL_TYPES.splice(0, CHANNEL_TYPES.length, ...response.response); - } - }); + services.notificationsService.getServerFeatures().then((response) => { + if (response.ok) { + CHANNEL_TYPES.splice(0, CHANNEL_TYPES.length, ...response.response); + } + }); - getPlugins(services.opensearchService).then((plugins): void => { - setIsNotificationPluginInstalled(plugins.includes(OS_NOTIFICATION_PLUGIN)); + getPlugins(services.opensearchService).then((plugins): void => { + setIsNotificationPluginInstalled(plugins.includes(OS_NOTIFICATION_PLUGIN)); + }); + }) + .catch((error) => { + console.error(error.message ?? error); }); - }); return () => ReactDOM.unmountComponentAtNode(params.element); } diff --git a/public/services/utils/constants.ts b/public/services/utils/constants.ts index e32ea73a..f85015c6 100644 --- a/public/services/utils/constants.ts +++ b/public/services/utils/constants.ts @@ -36,9 +36,7 @@ export const [getBreadCrumbsSetter, setBreadCrumbsSetter] = createGetterSetter< CoreStart['chrome']['setBreadcrumbs'] >('breadCrumbSetter'); -export const [getChrome, setChrome] = createGetterSetter< - CoreStart['chrome'] ->('chrome'); +export const [getChrome, setChrome] = createGetterSetter('chrome'); export const [getContentManagement, setContentManagement] = createGetterSetter< ContentManagementPluginStart @@ -59,4 +57,4 @@ export const [getSavedObjectsClient, setSavedObjectsClient] = createGetterSetter export const [ getDataSourceManagementPlugin, setDataSourceManagementPlugin, -] = createNullableGetterSetter(); +] = createNullableGetterSetter(); diff --git a/public/store/LogTypeStore.ts b/public/store/LogTypeStore.ts index 5dfa8602..f1fb07cd 100644 --- a/public/store/LogTypeStore.ts +++ b/public/store/LogTypeStore.ts @@ -9,7 +9,11 @@ import LogTypeService from '../services/LogTypeService'; import { errorNotificationToast } from '../utils/helpers'; import { DataStore } from './DataStore'; import { ruleTypes } from '../pages/Rules/utils/constants'; -import { logTypeCategories, logTypesByCategories } from '../utils/constants'; +import { + DATA_SOURCE_NOT_SET_ERROR, + logTypeCategories, + logTypesByCategories, +} from '../utils/constants'; import { getLogTypeLabel } from '../pages/LogTypes/utils/helpers'; export class LogTypeStore { @@ -41,58 +45,72 @@ export class LogTypeStore { } public async getLogTypes(): Promise { - const logTypesRes = await this.service.searchLogTypes(); - if (logTypesRes.ok) { - const logTypes: LogType[] = logTypesRes.response.hits.hits.map((hit) => { - return { - id: hit._id, - ...hit._source, - source: hit._source.source.toLowerCase() === 'sigma' ? 'Standard' : hit._source.source, - }; - }); + try { + const logTypesRes = await this.service.searchLogTypes(); + if (logTypesRes.ok) { + const logTypes: LogType[] = logTypesRes.response.hits.hits.map((hit) => { + return { + id: hit._id, + ...hit._source, + source: hit._source.source.toLowerCase() === 'sigma' ? 'Standard' : hit._source.source, + }; + }); - ruleTypes.splice( - 0, - ruleTypes.length, - ...logTypes - .map(({ category, id, name, source }) => ({ - label: getLogTypeLabel(name), - value: name, - id, - category, - isStandard: source === 'Standard', - })) - .sort((a, b) => { - return a.label < b.label ? -1 : a.label > b.label ? 1 : 0; + ruleTypes.splice( + 0, + ruleTypes.length, + ...logTypes + .map(({ category, id, name, source }) => ({ + label: getLogTypeLabel(name), + value: name, + id, + category, + isStandard: source === 'Standard', + })) + .sort((a, b) => { + return a.label < b.label ? -1 : a.label > b.label ? 1 : 0; + }) + ); + + // Set log category types + for (const key in logTypesByCategories) { + delete logTypesByCategories[key]; + } + logTypes.forEach((logType) => { + logTypesByCategories[logType.category] = logTypesByCategories[logType.category] || []; + logTypesByCategories[logType.category].push(logType); + }); + logTypeCategories.splice( + 0, + logTypeCategories.length, + ...Object.keys(logTypesByCategories).sort((a, b) => { + if (a === 'Other') { + return 1; + } else if (b === 'Other') { + return -1; + } else { + return a < b ? -1 : a > b ? 1 : 0; + } }) - ); + ); - // Set log category types - for (const key in logTypesByCategories) { - delete logTypesByCategories[key]; + return logTypes; } - logTypes.forEach((logType) => { - logTypesByCategories[logType.category] = logTypesByCategories[logType.category] || []; - logTypesByCategories[logType.category].push(logType); - }); - logTypeCategories.splice( - 0, - logTypeCategories.length, - ...Object.keys(logTypesByCategories).sort((a, b) => { - if (a === 'Other') { - return 1; - } else if (b === 'Other') { - return -1; - } else { - return a < b ? -1 : a > b ? 1 : 0; - } - }) - ); - - return logTypes; - } - return []; + return []; + } catch (error: any) { + if (error.message === DATA_SOURCE_NOT_SET_ERROR) { + errorNotificationToast( + this.notifications, + 'Fetch', + 'Log types', + 'Select valid data source.' + ); + return []; + } + + throw error; + } } public async createLogType(logType: LogTypeBase): Promise { diff --git a/public/utils/constants.ts b/public/utils/constants.ts index 6c04d8f9..afae860c 100644 --- a/public/utils/constants.ts +++ b/public/utils/constants.ts @@ -317,3 +317,5 @@ const LocalCluster: DataSourceOption = { }; export const dataSourceObservable = new BehaviorSubject(LocalCluster); + +export const DATA_SOURCE_NOT_SET_ERROR = 'Data source is not set'; diff --git a/public/utils/helpers.tsx b/public/utils/helpers.tsx index fdf5f2bb..378db069 100644 --- a/public/utils/helpers.tsx +++ b/public/utils/helpers.tsx @@ -331,6 +331,9 @@ export const errorNotificationToast = ( errorMessage: string = '', displayTime: number = 5000 // 5 seconds; default is 10 seconds ) => { + if (errorMessage.toLowerCase().includes('no living connections')) { + return; + } const message = `Failed to ${actionName} ${objectName}:`; console.error(message, errorMessage); notifications?.toasts.addDanger({