From 7fdbe565763f1498488eec8d5a1cde29a74fb4ec Mon Sep 17 00:00:00 2001 From: Maximiliano Ibarra Date: Mon, 7 Jun 2021 16:23:52 -0300 Subject: [PATCH 01/24] Feature/3316 error handler orchestrator (#3327) * feat(errorBoundary): Added ErrorBoundary HOC and component and added loglevel dependency * feature(errorBoundary): Moved with the others HOCs. * feature(errorBoundary): Typo refactor. * First attempt LoggerService * Merged error boundary, integrated loggerService. * changed logger name, create logger-service test file * Updated CHANGELOG * Moved to react-services, changed name, traslates comments * feat(errorBoundary): Removed old integration * refactor(loggerService): Changed class for function methods. * test(logger-service): Added basic unit test to logger-service * refactor(logger-service): Applied new implementation of error-orchestrator service. * feature(logger-service): PR comments and some refactors. Co-authored-by: gabiwassan Co-authored-by: Ibarra Maximiliano --- CHANGELOG.md | 3 + common/constants.ts | 33 ++++++++++ package.json | 3 +- public/kibana-services.ts | 10 ++- .../error-orchestrator-base.ts | 65 +++++++++++++++++++ .../error-orchestrator-business.ts | 20 ++++++ .../error-orchestrator-critical.ts | 20 ++++++ .../error-orchestrator-ui.ts | 24 +++++++ .../error-orchestrator.factory.ts | 29 +++++++++ .../error-orchestrator.service.ts | 23 +++++++ .../error-orchestrator/types.ts | 46 +++++++++++++ public/react-services/index.ts | 3 +- 12 files changed, 275 insertions(+), 4 deletions(-) create mode 100644 public/react-services/error-orchestrator/error-orchestrator-base.ts create mode 100644 public/react-services/error-orchestrator/error-orchestrator-business.ts create mode 100644 public/react-services/error-orchestrator/error-orchestrator-critical.ts create mode 100644 public/react-services/error-orchestrator/error-orchestrator-ui.ts create mode 100644 public/react-services/error-orchestrator/error-orchestrator.factory.ts create mode 100644 public/react-services/error-orchestrator/error-orchestrator.service.ts create mode 100644 public/react-services/error-orchestrator/types.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index ca9f0e2c74..d0d01a477d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ All notable changes to the Wazuh app project will be documented in this file. - Changed ossec to wazuh in sample-data [#3121](https://github.com/wazuh/wazuh-kibana-app/pull/3121) - Changed empty fields in FIM tables and `syscheck.value_name` in discovery now show an empty tag for visual clarity [#3279](https://github.com/wazuh/wazuh-kibana-app/pull/3279) +### Added +- Added new error handler to be responsible for the error orchestration [#3327](https://github.com/wazuh/wazuh-kibana-app/pull/3327) + ## Wazuh v4.2.0 - Kibana 7.10.2 , 7.11.2 - Revision 4201 ### Added diff --git a/common/constants.ts b/common/constants.ts index bc7cdcdb62..71b78dd6cb 100644 --- a/common/constants.ts +++ b/common/constants.ts @@ -220,3 +220,36 @@ export enum WAZUH_MODULES_ID{ export const AUTHORIZED_AGENTS = 'authorized-agents'; export const HEALTH_CHECK = 'health-check'; + +// Health check +export const HEALTH_CHECK_REDIRECTION_TIME = 300; //ms + +// Kibana settings +// Default timeTilter set by the app +export const WAZUH_KIBANA_SETTING_TIME_FILTER = { + from: "now-24h", + to: 'now' +}; +export const KIBANA_SETTING_NAME_TIME_FILTER = 'timepicker:timeDefaults'; + +// Default maxBuckets set by the app +export const WAZUH_KIBANA_SETTING_MAX_BUCKETS = 200000; +export const KIBANA_SETTING_NAME_MAX_BUCKETS = 'timelion:max_buckets'; + +// Default metaFields Kibana setting set by the app +export const WAZUH_KIBANA_SETTING_METAFIELDS = ['_source', '_index']; +export const KIBANA_SETTING_NAME_METAFIELDS = 'metaFields'; + + +// Logger +export const UI_LOGGER_LEVELS = { + WARNING: 'WARNING', + INFO: 'INFO', + ERROR: 'ERROR', +}; + +export const UI_TOAST_COLOR = { + SUCCESS: 'success', + WARNING: 'warning', + DANGER: 'danger', +}; diff --git a/package.json b/package.json index 197ab09459..1e189b4f85 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "test:browser": "plugin-helpers test:browser", "test:jest": "node scripts/jest", "generate:api-4.0-info": "cd scripts/generate-api-4.0-info;./generate-api-4.0-info.sh;cd ../..", - "prebuild": "node scripts/generate-build-version" + "prebuild": "node scripts/generate-build-version" }, "dependencies": { "angular-animate": "1.7.8", @@ -48,6 +48,7 @@ "js2xmlparser": "^3.0.0", "json2csv": "^4.1.2", "jwt-decode": "^2.2.0", + "loglevel": "^1.7.1", "needle": "^2.0.1", "node-cron": "^1.1.2", "pdfmake": "0.1.65", diff --git a/public/kibana-services.ts b/public/kibana-services.ts index 33fa421690..c7167d09b4 100644 --- a/public/kibana-services.ts +++ b/public/kibana-services.ts @@ -16,6 +16,7 @@ import { DataPublicPluginStart } from '../../../src/plugins/data/public'; import { VisualizationsStart } from '../../../src/plugins/visualizations/public'; import { NavigationPublicPluginStart } from '../../../src/plugins/navigation/public'; import { AppPluginStartDependencies } from './types'; +import { ErrorOrchestrator } from '../common/constants'; export const [getCore, setCore] = createGetterSetter('Core'); export const [getPlugins, setPlugins] = createGetterSetter('Plugins'); @@ -23,7 +24,9 @@ export const [getToasts, setToasts] = createGetterSetter('Toasts'); export const [getHttp, setHttp] = createGetterSetter('Http'); export const [getUiSettings, setUiSettings] = createGetterSetter('UiSettings'); export const [getChrome, setChrome] = createGetterSetter('Chrome'); -export const [getScopedHistory, setScopedHistory] = createGetterSetter('ScopedHistory'); +export const [getScopedHistory, setScopedHistory] = createGetterSetter( + 'ScopedHistory' +); export const [getOverlays, setOverlays] = createGetterSetter('Overlays'); export const [getSavedObjects, setSavedObjects] = createGetterSetter( 'SavedObjects' @@ -37,6 +40,9 @@ export const [getVisualizationsPlugin, setVisualizationsPlugin] = createGetterSe export const [getNavigationPlugin, setNavigationPlugin] = createGetterSetter< NavigationPublicPluginStart >('NavigationPlugin'); +export const [getErrorOrchestrator, setErrorOrchestrator] = createGetterSetter( + 'ErrorOrchestrator' +); /** * set bootstrapped inner angular module @@ -66,4 +72,4 @@ export function getDiscoverModule() { return discoverModule; } -export const [getCookies, setCookies] = createGetterSetter('Cookies'); \ No newline at end of file +export const [getCookies, setCookies] = createGetterSetter('Cookies'); diff --git a/public/react-services/error-orchestrator/error-orchestrator-base.ts b/public/react-services/error-orchestrator/error-orchestrator-base.ts new file mode 100644 index 0000000000..3a14aaba2c --- /dev/null +++ b/public/react-services/error-orchestrator/error-orchestrator-base.ts @@ -0,0 +1,65 @@ +/* + * Wazuh app - Error Orchestrator base + * Copyright (C) 2015-2021 Wazuh, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Find more information about this on the LICENSE file. + */ + +import { ErrorToastOptions } from 'kibana/public'; +import { UI_LOGGER_LEVELS } from '../../../common/constants'; +import { getToasts } from '../../kibana-services'; +import loglevel from 'loglevel'; +import { GenericRequest } from '../../react-services/generic-request'; +import { ErrorOrchestrator, UIErrorLog } from './types'; + +export class ErrorOrchestratorBase implements ErrorOrchestrator { + public loadErrorLog(errorLog: UIErrorLog) { + if (errorLog.display) this.displayError(errorLog); + if (errorLog.store) this.storeError(errorLog).then(loglevel.info); + } + + public displayError(errorLog: UIErrorLog) { + const toast = { + title: errorLog.error.title, + toastMessage: errorLog.error.message, + toastLifeTimeMs: 3000, + }; + + getToasts().addError(errorLog.error.error, toast as ErrorToastOptions); + } + + public loglevelError(errorLog: UIErrorLog) { + switch (errorLog.level) { + case UI_LOGGER_LEVELS.INFO: + loglevel.info(errorLog.error.message, errorLog.error.error); + break; + case UI_LOGGER_LEVELS.WARNING: + loglevel.warn(errorLog.error.message, errorLog.error.error); + break; + case UI_LOGGER_LEVELS.ERROR: + loglevel.error(errorLog.error.message, errorLog.error.error); + break; + default: + console.log('No error level', errorLog.error.message, errorLog.error.error); + } + } + + private async storeError(errorLog: UIErrorLog) { + try { + await GenericRequest.request('POST', `/utils/logs/ui`, { + body: { + message: errorLog.error.message, + level: errorLog.level, + location: errorLog.location, + }, + }); + } catch (error) { + loglevel.error('Failed on request [POST /utils/logs/ui]', error); + } + } +} diff --git a/public/react-services/error-orchestrator/error-orchestrator-business.ts b/public/react-services/error-orchestrator/error-orchestrator-business.ts new file mode 100644 index 0000000000..56d365c436 --- /dev/null +++ b/public/react-services/error-orchestrator/error-orchestrator-business.ts @@ -0,0 +1,20 @@ +/* + * Wazuh app - Error Orchestrator for business implementation + * Copyright (C) 2015-2021 Wazuh, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Find more information about this on the LICENSE file. + */ + +import { ErrorOrchestratorBase } from './error-orchestrator-base'; +import { UIErrorLog } from './types'; + +export class ErrorOrchestratorBusiness extends ErrorOrchestratorBase { + public displayError(errorLog: UIErrorLog) { + super.displayError(errorLog); + } +} diff --git a/public/react-services/error-orchestrator/error-orchestrator-critical.ts b/public/react-services/error-orchestrator/error-orchestrator-critical.ts new file mode 100644 index 0000000000..0cdafad8a3 --- /dev/null +++ b/public/react-services/error-orchestrator/error-orchestrator-critical.ts @@ -0,0 +1,20 @@ +/* + * Wazuh app - Error Orchestrator for critical implementation + * Copyright (C) 2015-2021 Wazuh, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Find more information about this on the LICENSE file. + */ + +import { ErrorOrchestratorBase } from './error-orchestrator-base'; +import { UIErrorLog } from './types'; + +export class ErrorOrchestratorCritical extends ErrorOrchestratorBase { + public displayError(errorLog: UIErrorLog) { + // continue here with the integration with the error-boundary hoc/component + } +} diff --git a/public/react-services/error-orchestrator/error-orchestrator-ui.ts b/public/react-services/error-orchestrator/error-orchestrator-ui.ts new file mode 100644 index 0000000000..4277b22ea5 --- /dev/null +++ b/public/react-services/error-orchestrator/error-orchestrator-ui.ts @@ -0,0 +1,24 @@ +/* + * Wazuh app - Error Orchestrator for UI implementation + * Copyright (C) 2015-2021 Wazuh, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Find more information about this on the LICENSE file. + */ + +import { ErrorOrchestratorBase } from './error-orchestrator-base'; +import { UIErrorLog } from './types'; + +export class ErrorOrchestratorUI extends ErrorOrchestratorBase { + public displayError(errorLog: UIErrorLog) { + super.displayError(errorLog); + } + + public loglevelError(errorLog: UIErrorLog) { + super.loglevelError(errorLog); + } +} diff --git a/public/react-services/error-orchestrator/error-orchestrator.factory.ts b/public/react-services/error-orchestrator/error-orchestrator.factory.ts new file mode 100644 index 0000000000..1d71990a7b --- /dev/null +++ b/public/react-services/error-orchestrator/error-orchestrator.factory.ts @@ -0,0 +1,29 @@ +/* + * Wazuh app - Error Orchestrator factory + * Copyright (C) 2015-2021 Wazuh, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Find more information about this on the LICENSE file. + */ + +import { ErrorOrchestratorUI } from './error-orchestrator-ui'; +import { ErrorOrchestratorBusiness } from './error-orchestrator-business'; +import { ErrorOrchestratorCritical } from './error-orchestrator-critical'; +import { ErrorOrchestrator, UI_ERROR_SEVERITIES, UIErrorSeverity } from './types'; + +export const errorOrchestratorFactory = (severity: UIErrorSeverity): ErrorOrchestrator => { + switch (severity) { + case UI_ERROR_SEVERITIES.UI: + return new ErrorOrchestratorUI(); + case UI_ERROR_SEVERITIES.BUSINESS: + return new ErrorOrchestratorBusiness(); + case UI_ERROR_SEVERITIES.CRITICAL: + return new ErrorOrchestratorCritical(); + default: + break; + } +}; diff --git a/public/react-services/error-orchestrator/error-orchestrator.service.ts b/public/react-services/error-orchestrator/error-orchestrator.service.ts new file mode 100644 index 0000000000..241b0bba43 --- /dev/null +++ b/public/react-services/error-orchestrator/error-orchestrator.service.ts @@ -0,0 +1,23 @@ +/* + * Wazuh app - Error Orchestrator service + * Copyright (C) 2015-2021 Wazuh, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Find more information about this on the LICENSE file. + */ + +import { errorOrchestratorFactory } from './error-orchestrator.factory'; +import { ErrorOrchestrator, UIErrorLog } from './types'; + +export class ErrorOrchestratorClass { + public constructor() {} + + public handleError(uiErrorLog: UIErrorLog) { + const errorOrchestrator: ErrorOrchestrator = errorOrchestratorFactory(uiErrorLog.severity); + errorOrchestrator.loadErrorLog(uiErrorLog); + } +} diff --git a/public/react-services/error-orchestrator/types.ts b/public/react-services/error-orchestrator/types.ts new file mode 100644 index 0000000000..b3d97bcf84 --- /dev/null +++ b/public/react-services/error-orchestrator/types.ts @@ -0,0 +1,46 @@ +/* + * Wazuh app - Error logger types + * Copyright (C) 2015-2021 Wazuh, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Find more information about this on the LICENSE file. + */ + +type WARNING = 'WARNING'; +type INFO = 'INFO'; +type ERROR = 'ERROR'; +export type UILogLevel = WARNING | INFO | ERROR; + +type UI = 'UI'; +type BUSINESS = 'BUSINESS'; +type CRITICAL = 'CRITICAL'; +export type UIErrorSeverity = UI | BUSINESS | CRITICAL; +export const UI_ERROR_SEVERITIES = { + UI: 'UI', + BUSINESS: 'BUSINESS', + CRITICAL: 'CRITICAL', +}; + +export type UIError = { + message: string; + error: any; + title?: string; +}; + +export type UIErrorLog = { + context: string; + level: UILogLevel; + severity: UIErrorSeverity; + display?: boolean; + store?: boolean; + error: UIError; + location: string; +}; + +export type ErrorOrchestrator = { + loadErrorLog: (uiErrorLog: UIErrorLog) => void; +}; diff --git a/public/react-services/index.ts b/public/react-services/index.ts index a10bf3b43d..1d70404858 100644 --- a/public/react-services/index.ts +++ b/public/react-services/index.ts @@ -1,4 +1,5 @@ export { GenericRequest } from './generic-request'; export { WzRequest } from './wz-request'; export { ErrorHandler } from './error-handler'; -export { formatUIDate } from './time-service'; \ No newline at end of file +export { formatUIDate } from './time-service'; +export { ErrorOrchestratorClass } from './error-orchestrator/error-orchestrator.service'; From fcef3c7027ef8f35077f0ebfa8f5aae4ae39fca2 Mon Sep 17 00:00:00 2001 From: Gabriel Wassan Date: Mon, 7 Jun 2021 16:24:50 -0300 Subject: [PATCH 02/24] Added ErrorBoundary HOC and component. (#3321) * feat(errorBoundary): Added ErrorBoundary HOC and component and added loglevel dependency * feature(errorBoundary): Moved with the others HOCs. * feature(errorBoundary): Typo refactor. * feature(errorBoundary): Some refactors * feat(errorBoundary): PR comments and rollback agent-preview * doc(changelog): Update changelog * feat(errorBoundary): Rollback * feat(errorBoundary): Rollback * feat(errorBoundary): Rollback * feat(errorBoundary): Rollback * feat(errorBoundary): Refactor props, pr comments. * feat(errorBoundary): Added unit test for error boundary. * feat(errorBoundary): Separated error boundary component of hoc * doc(error-boundary): Fixed and added licenses blocks. --- CHANGELOG.md | 1 + .../error-boundary.test.tsx.snap | 145 ++++++++++++++++ .../error-boundary/error-boundary.test.tsx | 31 ++++ .../common/error-boundary/error-boundary.tsx | 78 +++++++++ .../with-error-boundary.test.tsx.snap | 155 ++++++++++++++++++ .../with-error-boundary.test.tsx | 26 +++ .../error-boundary/with-error-boundary.tsx | 20 +++ public/components/common/hocs/index.ts | 2 + .../agent/components/agents-preview.js | 16 +- 9 files changed, 466 insertions(+), 8 deletions(-) create mode 100644 public/components/common/error-boundary/__snapshots__/error-boundary.test.tsx.snap create mode 100644 public/components/common/error-boundary/error-boundary.test.tsx create mode 100644 public/components/common/error-boundary/error-boundary.tsx create mode 100644 public/components/common/hocs/error-boundary/__snapshots__/with-error-boundary.test.tsx.snap create mode 100644 public/components/common/hocs/error-boundary/with-error-boundary.test.tsx create mode 100644 public/components/common/hocs/error-boundary/with-error-boundary.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index d0d01a477d..cd3d7a548c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ All notable changes to the Wazuh app project will be documented in this file. ### Added - Added new error handler to be responsible for the error orchestration [#3327](https://github.com/wazuh/wazuh-kibana-app/pull/3327) +- Added `Error Boundary` HOC and Component to handle render errors. [#3321](https://github.com/wazuh/wazuh-kibana-app/pull/3321) ## Wazuh v4.2.0 - Kibana 7.10.2 , 7.11.2 - Revision 4201 diff --git a/public/components/common/error-boundary/__snapshots__/error-boundary.test.tsx.snap b/public/components/common/error-boundary/__snapshots__/error-boundary.test.tsx.snap new file mode 100644 index 0000000000..36bcd994ea --- /dev/null +++ b/public/components/common/error-boundary/__snapshots__/error-boundary.test.tsx.snap @@ -0,0 +1,145 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ErrorBoundary component renders correctly and check snapshot 1`] = ` + + + + } + iconType="faceSad" + title={ +

+ Something went wrong. +

+ } + > +
+ + + + + + +
+ + + + +

+ Something went wrong. +

+
+ +
+ + +
+ +
+
+ Error: I crashed! +
+ + in ComponentWithError + in ErrorBoundary (created by WrapperComponent) + in WrapperComponent +
+
+
+
+
+ + +
+ + + +`; diff --git a/public/components/common/error-boundary/error-boundary.test.tsx b/public/components/common/error-boundary/error-boundary.test.tsx new file mode 100644 index 0000000000..a96db0b6e5 --- /dev/null +++ b/public/components/common/error-boundary/error-boundary.test.tsx @@ -0,0 +1,31 @@ +import React from 'react'; +import { mount } from 'enzyme'; +import ErrorBoundary from './error-boundary'; + +jest.mock('loglevel'); + +describe('ErrorBoundary component', () => { + const ComponentWithError = () => { + throw new Error('I crashed!'); + return <>; + }; + + it('renders correctly and check snapshot', () => { + const wrapper = mount( + + + + ); + + expect(wrapper).toMatchSnapshot(); + }); + + it('should display an ErrorMessage if wrapped component throws', () => { + const wrapper = mount( + + + + ); + expect(wrapper.find('EuiTitle').find('h2').text().trim()).toBe('Something went wrong.'); + }); +}); diff --git a/public/components/common/error-boundary/error-boundary.tsx b/public/components/common/error-boundary/error-boundary.tsx new file mode 100644 index 0000000000..b88c79b85c --- /dev/null +++ b/public/components/common/error-boundary/error-boundary.tsx @@ -0,0 +1,78 @@ +/* +* Wazuh app - React component for catch and handles rendering errors. +* +* Copyright (C) 2015-2021 Wazuh, Inc. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* Find more information about this on the LICENSE file. +* +*/ + +import React, { Component } from 'react'; +import log from 'loglevel'; +import { EuiEmptyPrompt } from '@elastic/eui'; + +export default class ErrorBoundary extends Component { + constructor(props) { + super(props); + this.state = { errorTitle: null, errorInfo: null, style: null }; + } + + componentDidCatch = (errorTitle, errorInfo) => catchFunc(errorTitle, errorInfo, this); + + render() { + const { errorTitle, style, errorInfo }: Readonly = this.state; + + if (errorInfo) { + return ; + } + return this.props.children; + } +} + +const catchFunc = (errorTitle, errorInfo, ctx) => { + ctx.setState({ + errorTitle: errorTitle, + errorInfo: errorInfo, + }); + + log.error({ errorTitle, errorInfo }); +}; + +const ErrorComponent = (props: { errorTitle: any; errorInfo: any; style: any }) => { + const styles = { + error: { + borderTop: '1px solid #777', + borderBottom: '1px solid #777', + padding: '12px', + }, + }; + + return ( +
+
+ {props.errorTitle && props.errorTitle.toString()} +
+ {props.errorInfo.componentStack} +
+
+ ); +}; + +const HandleError = (props: { errorTitle: any; errorInfo: any; style: any }) => ( + Something went wrong.} + body={ + + } + /> +); diff --git a/public/components/common/hocs/error-boundary/__snapshots__/with-error-boundary.test.tsx.snap b/public/components/common/hocs/error-boundary/__snapshots__/with-error-boundary.test.tsx.snap new file mode 100644 index 0000000000..a107118897 --- /dev/null +++ b/public/components/common/hocs/error-boundary/__snapshots__/with-error-boundary.test.tsx.snap @@ -0,0 +1,155 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`withErrorBoundary hoc implementation renders correctly and check snapshot 1`] = ` + + + + + } + iconType="faceSad" + title={ +

+ Something went wrong. +

+ } + > +
+ + + + + + +
+ + + + +

+ Something went wrong. +

+
+ +
+ + +
+ +
+
+ Error: I crashed! +
+ + in ComponentWithError + in Unknown + in ErrorBoundary + in Unknown (created by WrapperComponent) + in WrapperComponent +
+
+
+
+
+ + +
+ + + + +`; diff --git a/public/components/common/hocs/error-boundary/with-error-boundary.test.tsx b/public/components/common/hocs/error-boundary/with-error-boundary.test.tsx new file mode 100644 index 0000000000..abd64b7c07 --- /dev/null +++ b/public/components/common/hocs/error-boundary/with-error-boundary.test.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { withErrorBoundary } from './with-error-boundary'; +import { mount } from 'enzyme'; + +jest.mock('loglevel'); + +describe('withErrorBoundary hoc implementation', () => { + const ComponentWithError = () => { + throw new Error('I crashed!'); + return <>; + } + + it('renders correctly and check snapshot', () => { + const ErrorComponentWithHoc = withErrorBoundary(() => ); + const wrapper = mount(); + + expect(wrapper).toMatchSnapshot(); + }); + + it('should display an ErrorMessage if wrapped HOC throws', () => { + const ErrorComponentWithHoc = withErrorBoundary(() => ); + const wrapper = mount(); + + expect(wrapper.find('EuiTitle').find('h2').text().trim()).toBe('Something went wrong.'); + }); +}); diff --git a/public/components/common/hocs/error-boundary/with-error-boundary.tsx b/public/components/common/hocs/error-boundary/with-error-boundary.tsx new file mode 100644 index 0000000000..5ddab9f13f --- /dev/null +++ b/public/components/common/hocs/error-boundary/with-error-boundary.tsx @@ -0,0 +1,20 @@ +/* + * Wazuh app - React HOCs handles rendering errors + * Copyright (C) 2015-2021 Wazuh, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Find more information about this on the LICENSE file. + */ + +import React from 'react'; +import ErrorBoundary from '../../error-boundary/error-boundary'; + +export const withErrorBoundary = (WrappedComponent) => (props) => ( + + + +); diff --git a/public/components/common/hocs/index.ts b/public/components/common/hocs/index.ts index 55eae872df..81653072c2 100644 --- a/public/components/common/hocs/index.ts +++ b/public/components/common/hocs/index.ts @@ -30,3 +30,5 @@ export { withButtonOpenOnClick } from './withButtonOpenOnClick'; export { withAgentSupportModule } from './withAgentSupportModule'; export { withUserLogged } from './withUserLogged'; + +export { withErrorBoundary } from './error-boundary/with-error-boundary'; diff --git a/public/controllers/agent/components/agents-preview.js b/public/controllers/agent/components/agents-preview.js index 9cb2c4ff5a..dbc8f9cdbf 100644 --- a/public/controllers/agent/components/agents-preview.js +++ b/public/controllers/agent/components/agents-preview.js @@ -120,7 +120,7 @@ export const AgentsPreview = compose( this.setState({ platforms: platformsModel, loading: false }); } catch (error) {} } - + removeFilters(){ this._isMount && this.setState({agentTableFilters: []}) } @@ -164,7 +164,7 @@ export const AgentsPreview = compose( {this.totalAgents > 0 && ( - + {this.summary && ( @@ -236,7 +236,7 @@ export const AgentsPreview = compose( - + this.props.tableProps.showAgent( this.lastAgent )}>{this.lastAgent.name} @@ -271,7 +271,7 @@ export const AgentsPreview = compose( style={{ whiteSpace: 'nowrap' }} titleSize="s" description="Most active agent" - titleColor="primary" + titleColor="primary" /> )} @@ -298,13 +298,13 @@ export const AgentsPreview = compose(
{this.props.resultState === 'loading' && ( -
+
) } - + - + - + )} From 5feef14e53c3855b9d0458eeca23a6a1b615bfb5 Mon Sep 17 00:00:00 2001 From: gabiwassan Date: Tue, 8 Jun 2021 09:43:01 -0300 Subject: [PATCH 03/24] feature(logger-service): PR comments --- .../common/error-boundary/error-boundary.tsx | 63 +++++++++++++------ .../error-orchestrator-critical.ts | 2 +- .../error-orchestrator/types.ts | 2 +- 3 files changed, 47 insertions(+), 20 deletions(-) diff --git a/public/components/common/error-boundary/error-boundary.tsx b/public/components/common/error-boundary/error-boundary.tsx index b88c79b85c..7957a597a9 100644 --- a/public/components/common/error-boundary/error-boundary.tsx +++ b/public/components/common/error-boundary/error-boundary.tsx @@ -1,25 +1,34 @@ /* -* Wazuh app - React component for catch and handles rendering errors. -* -* Copyright (C) 2015-2021 Wazuh, Inc. -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* Find more information about this on the LICENSE file. -* -*/ + * Wazuh app - React component for catch and handles rendering errors. + * + * Copyright (C) 2015-2021 Wazuh, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Find more information about this on the LICENSE file. + * + */ import React, { Component } from 'react'; -import log from 'loglevel'; +import loglevel from 'loglevel'; import { EuiEmptyPrompt } from '@elastic/eui'; +import { UI_LOGGER_LEVELS } from '../../../../common/constants'; +import { + UI_ERROR_SEVERITIES, + UIErrorLog, + UIErrorSeverity, + UILogLevel, +} from '../../../react-services/error-orchestrator/types'; +import { ErrorOrchestratorClass } from '../../../react-services'; export default class ErrorBoundary extends Component { constructor(props) { super(props); this.state = { errorTitle: null, errorInfo: null, style: null }; + this.context = this.constructor.displayName || this.constructor.name || undefined; } componentDidCatch = (errorTitle, errorInfo) => catchFunc(errorTitle, errorInfo, this); @@ -35,12 +44,30 @@ export default class ErrorBoundary extends Component { } const catchFunc = (errorTitle, errorInfo, ctx) => { - ctx.setState({ - errorTitle: errorTitle, - errorInfo: errorInfo, - }); + try { + ctx.setState({ + errorTitle: errorTitle, + errorInfo: errorInfo, + }); - log.error({ errorTitle, errorInfo }); + const options: UIErrorLog = { + context: ctx.context, + level: UI_LOGGER_LEVELS.WARNING as UILogLevel, + severity: UI_ERROR_SEVERITIES.UI as UIErrorSeverity, + display: true, + store: true, + error: { + error: errorTitle.name, + message: errorTitle.message, + title: errorTitle.toString(), + }, + }; + + const logger = new ErrorOrchestratorClass(); + logger.handleError(options); + } catch (error) { + loglevel.error('Logger failed: ', error); + } }; const ErrorComponent = (props: { errorTitle: any; errorInfo: any; style: any }) => { diff --git a/public/react-services/error-orchestrator/error-orchestrator-critical.ts b/public/react-services/error-orchestrator/error-orchestrator-critical.ts index 0cdafad8a3..894dfa905a 100644 --- a/public/react-services/error-orchestrator/error-orchestrator-critical.ts +++ b/public/react-services/error-orchestrator/error-orchestrator-critical.ts @@ -15,6 +15,6 @@ import { UIErrorLog } from './types'; export class ErrorOrchestratorCritical extends ErrorOrchestratorBase { public displayError(errorLog: UIErrorLog) { - // continue here with the integration with the error-boundary hoc/component + super.displayError(errorLog) } } diff --git a/public/react-services/error-orchestrator/types.ts b/public/react-services/error-orchestrator/types.ts index b3d97bcf84..cbe593a7de 100644 --- a/public/react-services/error-orchestrator/types.ts +++ b/public/react-services/error-orchestrator/types.ts @@ -38,7 +38,7 @@ export type UIErrorLog = { display?: boolean; store?: boolean; error: UIError; - location: string; + location?: string; }; export type ErrorOrchestrator = { From 457e0e540e7196efe54b1b58b8fc9d5c8c180f38 Mon Sep 17 00:00:00 2001 From: gabiwassan Date: Tue, 8 Jun 2021 16:14:23 -0300 Subject: [PATCH 04/24] feature(logger-orchestrator): Refactors on management of severity. --- .../error-orchestrator-base.ts | 27 +------------------ .../error-orchestrator-business.ts | 23 +++++++++++++++- .../error-orchestrator-critical.ts | 5 +++- .../error-orchestrator-ui.ts | 20 ++++++++++---- 4 files changed, 42 insertions(+), 33 deletions(-) diff --git a/public/react-services/error-orchestrator/error-orchestrator-base.ts b/public/react-services/error-orchestrator/error-orchestrator-base.ts index 3a14aaba2c..bc798d88e0 100644 --- a/public/react-services/error-orchestrator/error-orchestrator-base.ts +++ b/public/react-services/error-orchestrator/error-orchestrator-base.ts @@ -10,9 +10,6 @@ * Find more information about this on the LICENSE file. */ -import { ErrorToastOptions } from 'kibana/public'; -import { UI_LOGGER_LEVELS } from '../../../common/constants'; -import { getToasts } from '../../kibana-services'; import loglevel from 'loglevel'; import { GenericRequest } from '../../react-services/generic-request'; import { ErrorOrchestrator, UIErrorLog } from './types'; @@ -24,29 +21,7 @@ export class ErrorOrchestratorBase implements ErrorOrchestrator { } public displayError(errorLog: UIErrorLog) { - const toast = { - title: errorLog.error.title, - toastMessage: errorLog.error.message, - toastLifeTimeMs: 3000, - }; - - getToasts().addError(errorLog.error.error, toast as ErrorToastOptions); - } - - public loglevelError(errorLog: UIErrorLog) { - switch (errorLog.level) { - case UI_LOGGER_LEVELS.INFO: - loglevel.info(errorLog.error.message, errorLog.error.error); - break; - case UI_LOGGER_LEVELS.WARNING: - loglevel.warn(errorLog.error.message, errorLog.error.error); - break; - case UI_LOGGER_LEVELS.ERROR: - loglevel.error(errorLog.error.message, errorLog.error.error); - break; - default: - console.log('No error level', errorLog.error.message, errorLog.error.error); - } + throw new Error('Should be implemented!'); } private async storeError(errorLog: UIErrorLog) { diff --git a/public/react-services/error-orchestrator/error-orchestrator-business.ts b/public/react-services/error-orchestrator/error-orchestrator-business.ts index 56d365c436..851bb77b96 100644 --- a/public/react-services/error-orchestrator/error-orchestrator-business.ts +++ b/public/react-services/error-orchestrator/error-orchestrator-business.ts @@ -12,9 +12,30 @@ import { ErrorOrchestratorBase } from './error-orchestrator-base'; import { UIErrorLog } from './types'; +import { UI_LOGGER_LEVELS } from '../../../common/constants'; +import { getToasts } from '../../kibana-services'; +import { ErrorToastOptions } from 'kibana/public'; export class ErrorOrchestratorBusiness extends ErrorOrchestratorBase { public displayError(errorLog: UIErrorLog) { - super.displayError(errorLog); + const toast = { + title: errorLog.error.title, + toastMessage: errorLog.error.message, + toastLifeTimeMs: 3000, + }; + + switch (errorLog.level) { + case UI_LOGGER_LEVELS.INFO: + getToasts().addInfo(errorLog.error.error, toast as ErrorToastOptions); + break; + case UI_LOGGER_LEVELS.WARNING: + getToasts().addWarning(errorLog.error.error, toast as ErrorToastOptions); + break; + case UI_LOGGER_LEVELS.ERROR: + getToasts().addError(errorLog.error.error, toast as ErrorToastOptions); + break; + default: + console.log('No error level', errorLog.error.message, errorLog.error.error); + } } } diff --git a/public/react-services/error-orchestrator/error-orchestrator-critical.ts b/public/react-services/error-orchestrator/error-orchestrator-critical.ts index 894dfa905a..7ca96fbe4a 100644 --- a/public/react-services/error-orchestrator/error-orchestrator-critical.ts +++ b/public/react-services/error-orchestrator/error-orchestrator-critical.ts @@ -12,9 +12,12 @@ import { ErrorOrchestratorBase } from './error-orchestrator-base'; import { UIErrorLog } from './types'; +import { WzMisc } from '../../factories/misc'; export class ErrorOrchestratorCritical extends ErrorOrchestratorBase { public displayError(errorLog: UIErrorLog) { - super.displayError(errorLog) + const wzMisc = new WzMisc(); + wzMisc.setBlankScr(errorLog.error.message); + window.location.href = '#/blank-screen'; } } diff --git a/public/react-services/error-orchestrator/error-orchestrator-ui.ts b/public/react-services/error-orchestrator/error-orchestrator-ui.ts index 4277b22ea5..03d992b110 100644 --- a/public/react-services/error-orchestrator/error-orchestrator-ui.ts +++ b/public/react-services/error-orchestrator/error-orchestrator-ui.ts @@ -12,13 +12,23 @@ import { ErrorOrchestratorBase } from './error-orchestrator-base'; import { UIErrorLog } from './types'; +import { UI_LOGGER_LEVELS } from '../../../common/constants'; +import loglevel from 'loglevel'; export class ErrorOrchestratorUI extends ErrorOrchestratorBase { public displayError(errorLog: UIErrorLog) { - super.displayError(errorLog); - } - - public loglevelError(errorLog: UIErrorLog) { - super.loglevelError(errorLog); + switch (errorLog.level) { + case UI_LOGGER_LEVELS.INFO: + loglevel.info(errorLog.error.message, errorLog.error.error); + break; + case UI_LOGGER_LEVELS.WARNING: + loglevel.warn(errorLog.error.message, errorLog.error.error); + break; + case UI_LOGGER_LEVELS.ERROR: + loglevel.error(errorLog.error.message, errorLog.error.error); + break; + default: + console.log('No error level', errorLog.error.message, errorLog.error.error); + } } } From 76b10097211bbcd5b909a40221d98d066110eb9c Mon Sep 17 00:00:00 2001 From: gabiwassan Date: Tue, 8 Jun 2021 16:16:38 -0300 Subject: [PATCH 05/24] feature(logger-orchestrator): Refactor on wz-blank-screen component. --- .../wz-blank-screen/wz-blank-screen.js | 38 ++++++++----------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/public/components/wz-blank-screen/wz-blank-screen.js b/public/components/wz-blank-screen/wz-blank-screen.js index b919c68af1..0c00993409 100644 --- a/public/components/wz-blank-screen/wz-blank-screen.js +++ b/public/components/wz-blank-screen/wz-blank-screen.js @@ -9,14 +9,9 @@ * * Find more information about this on the LICENSE file. */ -import React, { Component, Fragment } from 'react'; -import { - EuiPage, - EuiPageContent, - EuiEmptyPrompt, - EuiButton, - EuiHorizontalRule -} from '@elastic/eui'; +import React, { Component } from 'react'; +import { EuiButton, EuiHorizontalRule, EuiPage, EuiPageContent } from '@elastic/eui'; +import { ErrorComponentPrompt } from '../common/error-boundary-prompt/error-boundary-prompt'; export class WzBlankScreen extends Component { constructor(props) { @@ -28,29 +23,28 @@ export class WzBlankScreen extends Component { return ( - {this.props.errorToShow || 'Something went wrong'}} - body={ - - +

- https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html + Elastic Guide
+
- https://documentation.wazuh.com/current/installation-guide/ + Wazuh installation guide

-
- } - actions={ - - Refresh - + + + Refresh + + } />
From f6d115cc6f3152d5f6945913e70b0b2ae201e793 Mon Sep 17 00:00:00 2001 From: gabiwassan Date: Tue, 8 Jun 2021 16:17:46 -0300 Subject: [PATCH 06/24] feature(logger-orchestrator): Separated prompt component from error-boundary. --- .../error-boundary-prompt.tsx | 34 ++++++++++++++++ .../common/error-boundary/error-boundary.tsx | 40 ++----------------- 2 files changed, 37 insertions(+), 37 deletions(-) create mode 100644 public/components/common/error-boundary-prompt/error-boundary-prompt.tsx diff --git a/public/components/common/error-boundary-prompt/error-boundary-prompt.tsx b/public/components/common/error-boundary-prompt/error-boundary-prompt.tsx new file mode 100644 index 0000000000..03276c3c37 --- /dev/null +++ b/public/components/common/error-boundary-prompt/error-boundary-prompt.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import { EuiEmptyPrompt } from '@elastic/eui'; + +export const ErrorComponentPrompt = (props: { + errorTitle: any; + errorInfo?: any; + style?: any; + action?: React.ReactNode; +}) => { + const styles = { + error: { + borderTop: '1px solid #777', + borderBottom: '1px solid #777', + padding: '12px', + }, + }; + + return ( + Something went wrong.} + body={ +
+
+ {props.errorTitle && props.errorTitle.toString()} +
+ {props.errorInfo?.componentStack || ''} +
+
+ } + actions={props.action || null} + /> + ); +}; diff --git a/public/components/common/error-boundary/error-boundary.tsx b/public/components/common/error-boundary/error-boundary.tsx index 7957a597a9..e95b0592a2 100644 --- a/public/components/common/error-boundary/error-boundary.tsx +++ b/public/components/common/error-boundary/error-boundary.tsx @@ -14,7 +14,6 @@ import React, { Component } from 'react'; import loglevel from 'loglevel'; -import { EuiEmptyPrompt } from '@elastic/eui'; import { UI_LOGGER_LEVELS } from '../../../../common/constants'; import { UI_ERROR_SEVERITIES, @@ -23,6 +22,7 @@ import { UILogLevel, } from '../../../react-services/error-orchestrator/types'; import { ErrorOrchestratorClass } from '../../../react-services'; +import { ErrorComponentPrompt } from '../error-boundary-prompt/error-boundary-prompt'; export default class ErrorBoundary extends Component { constructor(props) { @@ -37,7 +37,7 @@ export default class ErrorBoundary extends Component { const { errorTitle, style, errorInfo }: Readonly = this.state; if (errorInfo) { - return ; + return ; } return this.props.children; } @@ -54,7 +54,7 @@ const catchFunc = (errorTitle, errorInfo, ctx) => { context: ctx.context, level: UI_LOGGER_LEVELS.WARNING as UILogLevel, severity: UI_ERROR_SEVERITIES.UI as UIErrorSeverity, - display: true, + display: false, store: true, error: { error: errorTitle.name, @@ -69,37 +69,3 @@ const catchFunc = (errorTitle, errorInfo, ctx) => { loglevel.error('Logger failed: ', error); } }; - -const ErrorComponent = (props: { errorTitle: any; errorInfo: any; style: any }) => { - const styles = { - error: { - borderTop: '1px solid #777', - borderBottom: '1px solid #777', - padding: '12px', - }, - }; - - return ( -
-
- {props.errorTitle && props.errorTitle.toString()} -
- {props.errorInfo.componentStack} -
-
- ); -}; - -const HandleError = (props: { errorTitle: any; errorInfo: any; style: any }) => ( - Something went wrong.} - body={ - - } - /> -); From 5b64c9c619d2b075c12dd7ea862ea1886b8c6314 Mon Sep 17 00:00:00 2001 From: gabiwassan Date: Tue, 8 Jun 2021 16:19:35 -0300 Subject: [PATCH 07/24] feature(logger-orchestrator): Typo. --- public/components/wz-blank-screen/wz-blank-screen.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/components/wz-blank-screen/wz-blank-screen.js b/public/components/wz-blank-screen/wz-blank-screen.js index 0c00993409..d12e963d88 100644 --- a/public/components/wz-blank-screen/wz-blank-screen.js +++ b/public/components/wz-blank-screen/wz-blank-screen.js @@ -30,7 +30,7 @@ export class WzBlankScreen extends Component { <>

- Elastic Guide + Elastic guide

From 123e9503481dde17ebba96ab433d2350681c278d Mon Sep 17 00:00:00 2001 From: gabiwassan Date: Tue, 8 Jun 2021 16:22:44 -0300 Subject: [PATCH 08/24] test(error-boundary): Update snapshots. --- .../error-boundary.test.tsx.snap | 145 ---------------- .../with-error-boundary.test.tsx.snap | 155 ------------------ 2 files changed, 300 deletions(-) delete mode 100644 public/components/common/error-boundary/__snapshots__/error-boundary.test.tsx.snap delete mode 100644 public/components/common/hocs/error-boundary/__snapshots__/with-error-boundary.test.tsx.snap diff --git a/public/components/common/error-boundary/__snapshots__/error-boundary.test.tsx.snap b/public/components/common/error-boundary/__snapshots__/error-boundary.test.tsx.snap deleted file mode 100644 index 36bcd994ea..0000000000 --- a/public/components/common/error-boundary/__snapshots__/error-boundary.test.tsx.snap +++ /dev/null @@ -1,145 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`ErrorBoundary component renders correctly and check snapshot 1`] = ` - - - - } - iconType="faceSad" - title={ -

- Something went wrong. -

- } - > -
- - - - - - -
- - - - -

- Something went wrong. -

-
- -
- - -
- -
-
- Error: I crashed! -
- - in ComponentWithError - in ErrorBoundary (created by WrapperComponent) - in WrapperComponent -
-
-
-
-
- - -
- - - -`; diff --git a/public/components/common/hocs/error-boundary/__snapshots__/with-error-boundary.test.tsx.snap b/public/components/common/hocs/error-boundary/__snapshots__/with-error-boundary.test.tsx.snap deleted file mode 100644 index a107118897..0000000000 --- a/public/components/common/hocs/error-boundary/__snapshots__/with-error-boundary.test.tsx.snap +++ /dev/null @@ -1,155 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`withErrorBoundary hoc implementation renders correctly and check snapshot 1`] = ` - - - - - } - iconType="faceSad" - title={ -

- Something went wrong. -

- } - > -
- - - - - - -
- - - - -

- Something went wrong. -

-
- -
- - -
- -
-
- Error: I crashed! -
- - in ComponentWithError - in Unknown - in ErrorBoundary - in Unknown (created by WrapperComponent) - in WrapperComponent -
-
-
-
-
- - -
- - - - -`; From bfc6dfa2bc12fcb307add6bdeb9e0ea880595c15 Mon Sep 17 00:00:00 2001 From: gabiwassan Date: Tue, 8 Jun 2021 16:23:44 -0300 Subject: [PATCH 09/24] test(error-boundary): Update snapshots --- .../error-boundary.test.tsx.snap | 145 +++++++++++++++++ .../with-error-boundary.test.tsx.snap | 153 ++++++++++++++++++ 2 files changed, 298 insertions(+) create mode 100644 public/components/common/error-boundary/__snapshots__/error-boundary.test.tsx.snap create mode 100644 public/components/common/hocs/error-boundary/__snapshots__/with-error-boundary.test.tsx.snap diff --git a/public/components/common/error-boundary/__snapshots__/error-boundary.test.tsx.snap b/public/components/common/error-boundary/__snapshots__/error-boundary.test.tsx.snap new file mode 100644 index 0000000000..98fbce8e8f --- /dev/null +++ b/public/components/common/error-boundary/__snapshots__/error-boundary.test.tsx.snap @@ -0,0 +1,145 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ErrorBoundary component renders correctly and check snapshot 1`] = ` + + + +
+ Error: I crashed! +
+ + in ComponentWithError + in ErrorBoundary (created by WrapperComponent) + in WrapperComponent +
+
+ } + iconType="faceSad" + title={ +

+ Something went wrong. +

+ } + > +
+ + + + + + +
+ + + + +

+ Something went wrong. +

+
+ +
+ + +
+
+
+ Error: I crashed! +
+ + in ComponentWithError + in ErrorBoundary (created by WrapperComponent) + in WrapperComponent +
+
+
+
+ + +
+ + + +`; diff --git a/public/components/common/hocs/error-boundary/__snapshots__/with-error-boundary.test.tsx.snap b/public/components/common/hocs/error-boundary/__snapshots__/with-error-boundary.test.tsx.snap new file mode 100644 index 0000000000..4225368f1c --- /dev/null +++ b/public/components/common/hocs/error-boundary/__snapshots__/with-error-boundary.test.tsx.snap @@ -0,0 +1,153 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`withErrorBoundary hoc implementation renders correctly and check snapshot 1`] = ` + + + + +
+ Error: I crashed! +
+ + in ComponentWithError + in Unknown + in ErrorBoundary + in Unknown (created by WrapperComponent) + in WrapperComponent +
+
+ } + iconType="faceSad" + title={ +

+ Something went wrong. +

+ } + > +
+ + + + + + +
+ + + + +

+ Something went wrong. +

+
+ +
+ + +
+
+
+ Error: I crashed! +
+ + in ComponentWithError + in Unknown + in ErrorBoundary + in Unknown (created by WrapperComponent) + in WrapperComponent +
+
+
+
+ + +
+ + + + +`; From d8c0f96750f6f93cee1ad57dbbdc7e24e881567b Mon Sep 17 00:00:00 2001 From: gabiwassan Date: Wed, 9 Jun 2021 11:37:00 -0300 Subject: [PATCH 10/24] fix(logger-orchestrator): PR comments and refactors, fix unit tests. --- .../error-boundary-prompt.tsx | 18 +++++++++++++-- .../error-boundary.test.tsx.snap | 20 ++++++++++++----- .../error-boundary/error-boundary.test.tsx | 10 +++++++-- .../common/error-boundary/error-boundary.tsx | 22 +++++++++++++++---- .../with-error-boundary.test.tsx.snap | 20 ++++++++++++----- .../with-error-boundary.test.tsx | 11 +++++++--- .../wz-blank-screen/wz-blank-screen.js | 2 +- .../error-orchestrator.service.ts | 2 +- public/react-services/index.ts | 2 +- 9 files changed, 81 insertions(+), 26 deletions(-) diff --git a/public/components/common/error-boundary-prompt/error-boundary-prompt.tsx b/public/components/common/error-boundary-prompt/error-boundary-prompt.tsx index 03276c3c37..b4bb7f1ab7 100644 --- a/public/components/common/error-boundary-prompt/error-boundary-prompt.tsx +++ b/public/components/common/error-boundary-prompt/error-boundary-prompt.tsx @@ -1,3 +1,17 @@ +/* + * Wazuh app - React Prompt handles rendering errors. + * + * Copyright (C) 2015-2021 Wazuh, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Find more information about this on the LICENSE file. + * + */ + import React from 'react'; import { EuiEmptyPrompt } from '@elastic/eui'; @@ -22,9 +36,9 @@ export const ErrorComponentPrompt = (props: { body={
- {props.errorTitle && props.errorTitle.toString()} + {props.errorTitle && props.errorTitle.toString()}
- {props.errorInfo?.componentStack || ''} + {props.errorInfo?.componentStack || ''}
} diff --git a/public/components/common/error-boundary/__snapshots__/error-boundary.test.tsx.snap b/public/components/common/error-boundary/__snapshots__/error-boundary.test.tsx.snap index 98fbce8e8f..0c0bd88dd0 100644 --- a/public/components/common/error-boundary/__snapshots__/error-boundary.test.tsx.snap +++ b/public/components/common/error-boundary/__snapshots__/error-boundary.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`ErrorBoundary component renders correctly and check snapshot 1`] = ` +exports[`ErrorBoundary component renders correctly to match the snapshot 1`] = ` - Error: I crashed! + + Error: I crashed! I crash very hard +
- + + in ComponentWithError in ErrorBoundary (created by WrapperComponent) in WrapperComponent +
} @@ -126,12 +130,16 @@ exports[`ErrorBoundary component renders correctly and check snapshot 1`] = ` } } > - Error: I crashed! + + Error: I crashed! I crash very hard +
- + + in ComponentWithError in ErrorBoundary (created by WrapperComponent) in WrapperComponent +
diff --git a/public/components/common/error-boundary/error-boundary.test.tsx b/public/components/common/error-boundary/error-boundary.test.tsx index a96db0b6e5..239d9e5e07 100644 --- a/public/components/common/error-boundary/error-boundary.test.tsx +++ b/public/components/common/error-boundary/error-boundary.test.tsx @@ -6,11 +6,11 @@ jest.mock('loglevel'); describe('ErrorBoundary component', () => { const ComponentWithError = () => { - throw new Error('I crashed!'); + throw new Error('I crashed! I crash very hard'); return <>; }; - it('renders correctly and check snapshot', () => { + it('renders correctly to match the snapshot', () => { const wrapper = mount( @@ -26,6 +26,12 @@ describe('ErrorBoundary component', () => { ); + + expect(wrapper.find('EuiTitle').exists()).toBeTruthy(); + expect(wrapper.find('EuiText').exists('details')).toBeTruthy(); expect(wrapper.find('EuiTitle').find('h2').text().trim()).toBe('Something went wrong.'); + expect(wrapper.find('EuiText').find('details').find('span').at(0).text()).toBe( + 'Error: I crashed! I crash very hard' + ); }); }); diff --git a/public/components/common/error-boundary/error-boundary.tsx b/public/components/common/error-boundary/error-boundary.tsx index e95b0592a2..395f9f3152 100644 --- a/public/components/common/error-boundary/error-boundary.tsx +++ b/public/components/common/error-boundary/error-boundary.tsx @@ -21,14 +21,16 @@ import { UIErrorSeverity, UILogLevel, } from '../../../react-services/error-orchestrator/types'; -import { ErrorOrchestratorClass } from '../../../react-services'; +import { ErrorOrchestratorService } from '../../../react-services'; import { ErrorComponentPrompt } from '../error-boundary-prompt/error-boundary-prompt'; export default class ErrorBoundary extends Component { + private logger: ErrorOrchestratorService; constructor(props) { super(props); this.state = { errorTitle: null, errorInfo: null, style: null }; this.context = this.constructor.displayName || this.constructor.name || undefined; + this.logger = new ErrorOrchestratorService(); } componentDidCatch = (errorTitle, errorInfo) => catchFunc(errorTitle, errorInfo, this); @@ -63,9 +65,21 @@ const catchFunc = (errorTitle, errorInfo, ctx) => { }, }; - const logger = new ErrorOrchestratorClass(); - logger.handleError(options); + ctx.logger.handleError(options); } catch (error) { - loglevel.error('Logger failed: ', error); + const optionsCatch: UIErrorLog = { + context: ctx.context, + level: UI_LOGGER_LEVELS.ERROR as UILogLevel, + severity: UI_ERROR_SEVERITIES.UI as UIErrorSeverity, + display: false, + store: true, + error: { + error: error, + message: error?.message || '', + title: '', + }, + }; + + ctx.logger.handleError(optionsCatch); } }; diff --git a/public/components/common/hocs/error-boundary/__snapshots__/with-error-boundary.test.tsx.snap b/public/components/common/hocs/error-boundary/__snapshots__/with-error-boundary.test.tsx.snap index 4225368f1c..24212ec32e 100644 --- a/public/components/common/hocs/error-boundary/__snapshots__/with-error-boundary.test.tsx.snap +++ b/public/components/common/hocs/error-boundary/__snapshots__/with-error-boundary.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`withErrorBoundary hoc implementation renders correctly and check snapshot 1`] = ` +exports[`withErrorBoundary hoc implementation renders correctly to match the snapshot 1`] = ` - Error: I crashed! + + Error: I crashed! I crash very hard +
- + + in ComponentWithError in Unknown in ErrorBoundary in Unknown (created by WrapperComponent) in WrapperComponent +
} @@ -131,14 +135,18 @@ exports[`withErrorBoundary hoc implementation renders correctly and check snapsh } } > - Error: I crashed! + + Error: I crashed! I crash very hard +
- + + in ComponentWithError in Unknown in ErrorBoundary in Unknown (created by WrapperComponent) in WrapperComponent +
diff --git a/public/components/common/hocs/error-boundary/with-error-boundary.test.tsx b/public/components/common/hocs/error-boundary/with-error-boundary.test.tsx index abd64b7c07..6b6e7c37c6 100644 --- a/public/components/common/hocs/error-boundary/with-error-boundary.test.tsx +++ b/public/components/common/hocs/error-boundary/with-error-boundary.test.tsx @@ -6,11 +6,11 @@ jest.mock('loglevel'); describe('withErrorBoundary hoc implementation', () => { const ComponentWithError = () => { - throw new Error('I crashed!'); + throw new Error('I crashed! I crash very hard'); return <>; - } + }; - it('renders correctly and check snapshot', () => { + it('renders correctly to match the snapshot', () => { const ErrorComponentWithHoc = withErrorBoundary(() => ); const wrapper = mount(); @@ -21,6 +21,11 @@ describe('withErrorBoundary hoc implementation', () => { const ErrorComponentWithHoc = withErrorBoundary(() => ); const wrapper = mount(); + expect(wrapper.find('EuiTitle').exists()).toBeTruthy(); + expect(wrapper.find('EuiText').exists('details')).toBeTruthy(); expect(wrapper.find('EuiTitle').find('h2').text().trim()).toBe('Something went wrong.'); + expect(wrapper.find('EuiText').find('details').find('span').at(0).text()).toBe( + 'Error: I crashed! I crash very hard' + ); }); }); diff --git a/public/components/wz-blank-screen/wz-blank-screen.js b/public/components/wz-blank-screen/wz-blank-screen.js index d12e963d88..919c9b0349 100644 --- a/public/components/wz-blank-screen/wz-blank-screen.js +++ b/public/components/wz-blank-screen/wz-blank-screen.js @@ -34,7 +34,7 @@ export class WzBlankScreen extends Component {

- + Wazuh installation guide

diff --git a/public/react-services/error-orchestrator/error-orchestrator.service.ts b/public/react-services/error-orchestrator/error-orchestrator.service.ts index 241b0bba43..5537693d32 100644 --- a/public/react-services/error-orchestrator/error-orchestrator.service.ts +++ b/public/react-services/error-orchestrator/error-orchestrator.service.ts @@ -13,7 +13,7 @@ import { errorOrchestratorFactory } from './error-orchestrator.factory'; import { ErrorOrchestrator, UIErrorLog } from './types'; -export class ErrorOrchestratorClass { +export class ErrorOrchestratorService { public constructor() {} public handleError(uiErrorLog: UIErrorLog) { diff --git a/public/react-services/index.ts b/public/react-services/index.ts index 1d70404858..0b3f7801c1 100644 --- a/public/react-services/index.ts +++ b/public/react-services/index.ts @@ -2,4 +2,4 @@ export { GenericRequest } from './generic-request'; export { WzRequest } from './wz-request'; export { ErrorHandler } from './error-handler'; export { formatUIDate } from './time-service'; -export { ErrorOrchestratorClass } from './error-orchestrator/error-orchestrator.service'; +export { ErrorOrchestratorService } from './error-orchestrator/error-orchestrator.service'; From 05a95a48c529c479a5a70417ec10bf396fb609c9 Mon Sep 17 00:00:00 2001 From: gabiwassan Date: Wed, 9 Jun 2021 15:20:28 -0300 Subject: [PATCH 11/24] test(error-orchestrator-base): Added simple unit test. Fixed licence block. --- .../error-boundary/error-boundary.test.tsx | 14 ++++++ .../error-orchestrator-base.test.ts | 44 +++++++++++++++++++ .../error-orchestrator-base.ts | 2 +- .../wz-user-permissions.test.ts | 2 +- 4 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 public/react-services/error-orchestrator/error-orchestrator-base.test.ts diff --git a/public/components/common/error-boundary/error-boundary.test.tsx b/public/components/common/error-boundary/error-boundary.test.tsx index 239d9e5e07..cd5ca53a33 100644 --- a/public/components/common/error-boundary/error-boundary.test.tsx +++ b/public/components/common/error-boundary/error-boundary.test.tsx @@ -1,3 +1,17 @@ +/* + * Wazuh app - React test for ErrorBoundary component. + * + * Copyright (C) 2015-2021 Wazuh, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Find more information about this on the LICENSE file. + * + */ + import React from 'react'; import { mount } from 'enzyme'; import ErrorBoundary from './error-boundary'; diff --git a/public/react-services/error-orchestrator/error-orchestrator-base.test.ts b/public/react-services/error-orchestrator/error-orchestrator-base.test.ts new file mode 100644 index 0000000000..9a0d5a7e48 --- /dev/null +++ b/public/react-services/error-orchestrator/error-orchestrator-base.test.ts @@ -0,0 +1,44 @@ +/* + * Wazuh app - React test for ErrorOrchestratorBase. + * + * Copyright (C) 2015-2021 Wazuh, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Find more information about this on the LICENSE file. + * + */ + +import { ErrorOrchestratorBase } from './error-orchestrator-base'; +import { UIErrorLog } from './types'; + +describe('Wazuh Error Orchestrator Base', () => { + describe('Given a valid options params ', () => { + it('Should be called displayError and storeError', () => { + const options: UIErrorLog = { + context: 'unitTest', + level: 'ERROR', + severity: 'UI', + display: true, + store: true, + error: { + error: 'error name test1', + message: 'message test1', + title: 'title jest testing1', + }, + }; + const errorOrchestratorBase = new ErrorOrchestratorBase(); + const myDisplayError = (ErrorOrchestratorBase.prototype.displayError = jest.fn()); + const myStoreError = jest.spyOn(ErrorOrchestratorBase.prototype as any, 'storeError'); + myStoreError.mockImplementation(() => {}) + + errorOrchestratorBase.loadErrorLog(options); + + expect(myDisplayError).toBeCalledTimes(1); + expect(myStoreError).toBeCalledTimes(1); + }); + }); +}); diff --git a/public/react-services/error-orchestrator/error-orchestrator-base.ts b/public/react-services/error-orchestrator/error-orchestrator-base.ts index bc798d88e0..61592d8ba9 100644 --- a/public/react-services/error-orchestrator/error-orchestrator-base.ts +++ b/public/react-services/error-orchestrator/error-orchestrator-base.ts @@ -17,7 +17,7 @@ import { ErrorOrchestrator, UIErrorLog } from './types'; export class ErrorOrchestratorBase implements ErrorOrchestrator { public loadErrorLog(errorLog: UIErrorLog) { if (errorLog.display) this.displayError(errorLog); - if (errorLog.store) this.storeError(errorLog).then(loglevel.info); + if (errorLog.store) this.storeError(errorLog); } public displayError(errorLog: UIErrorLog) { diff --git a/public/react-services/wz-user-permissions.test.ts b/public/react-services/wz-user-permissions.test.ts index fe2cb6f8a5..b204c83b39 100644 --- a/public/react-services/wz-user-permissions.test.ts +++ b/public/react-services/wz-user-permissions.test.ts @@ -1,5 +1,5 @@ /* - * Wazuh app - React hook for get query of Kibana searchBar + * Wazuh app - React test for wz-user-permissions * Copyright (C) 2015-2021 Wazuh, Inc. * * This program is free software; you can redistribute it and/or modify From e481167204365d8d94457d05189f51a143bf8eb3 Mon Sep 17 00:00:00 2001 From: gabiwassan Date: Wed, 9 Jun 2021 15:47:29 -0300 Subject: [PATCH 12/24] test(error-orchestrator-base): Added simple unit test to ErrorOrchestratorCritical --- .../error-orchestrator-base.test.ts | 16 +++---- .../error-orchestrator-critical.test.ts | 44 +++++++++++++++++++ 2 files changed, 52 insertions(+), 8 deletions(-) create mode 100644 public/react-services/error-orchestrator/error-orchestrator-critical.test.ts diff --git a/public/react-services/error-orchestrator/error-orchestrator-base.test.ts b/public/react-services/error-orchestrator/error-orchestrator-base.test.ts index 9a0d5a7e48..b90361c916 100644 --- a/public/react-services/error-orchestrator/error-orchestrator-base.test.ts +++ b/public/react-services/error-orchestrator/error-orchestrator-base.test.ts @@ -1,5 +1,5 @@ /* - * Wazuh app - React test for ErrorOrchestratorBase. + * Wazuh app - Unit test for ErrorOrchestratorBase. * * Copyright (C) 2015-2021 Wazuh, Inc. * @@ -13,7 +13,7 @@ */ import { ErrorOrchestratorBase } from './error-orchestrator-base'; -import { UIErrorLog } from './types'; +import { ErrorOrchestrator, UIErrorLog } from './types'; describe('Wazuh Error Orchestrator Base', () => { describe('Given a valid options params ', () => { @@ -30,15 +30,15 @@ describe('Wazuh Error Orchestrator Base', () => { title: 'title jest testing1', }, }; - const errorOrchestratorBase = new ErrorOrchestratorBase(); - const myDisplayError = (ErrorOrchestratorBase.prototype.displayError = jest.fn()); - const myStoreError = jest.spyOn(ErrorOrchestratorBase.prototype as any, 'storeError'); - myStoreError.mockImplementation(() => {}) + const errorOrchestratorBase: ErrorOrchestrator = new ErrorOrchestratorBase(); + const mockDisplayError = (ErrorOrchestratorBase.prototype.displayError = jest.fn()); + const mockStoreError = jest.spyOn(ErrorOrchestratorBase.prototype as any, 'storeError'); + mockStoreError.mockImplementation(() => {}) errorOrchestratorBase.loadErrorLog(options); - expect(myDisplayError).toBeCalledTimes(1); - expect(myStoreError).toBeCalledTimes(1); + expect(mockDisplayError).toBeCalledTimes(1); + expect(mockStoreError).toBeCalledTimes(1); }); }); }); diff --git a/public/react-services/error-orchestrator/error-orchestrator-critical.test.ts b/public/react-services/error-orchestrator/error-orchestrator-critical.test.ts new file mode 100644 index 0000000000..87531d7aae --- /dev/null +++ b/public/react-services/error-orchestrator/error-orchestrator-critical.test.ts @@ -0,0 +1,44 @@ +/* + * Wazuh app - Unit test for ErrorOrchestratorCritical. + * + * Copyright (C) 2015-2021 Wazuh, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Find more information about this on the LICENSE file. + * + */ + +import { ErrorOrchestrator, UIErrorLog } from './types'; +import { ErrorOrchestratorCritical } from './error-orchestrator-critical'; +import { WzMisc } from '../../factories/misc'; + +describe('Wazuh Error Orchestrator Critical', () => { + describe('Given a valid options params ', () => { + it('Should be called mockSetBlankScr and redirect to BlankScreen', () => { + const options: UIErrorLog = { + context: 'unitTest', + level: 'ERROR', + severity: 'UI', + display: true, + store: false, + error: { + error: 'error name test1', + message: 'message test1', + title: 'title jest testing1', + }, + }; + + const mockSetBlankScr = (WzMisc.prototype.setBlankScr = jest.fn()); + + const errorOrchestratorCritical: ErrorOrchestrator = new ErrorOrchestratorCritical(); + errorOrchestratorCritical.loadErrorLog(options); + + expect(mockSetBlankScr).toBeCalledTimes(1); + expect(window.location.href).toEqual('http://localhost/#/blank-screen'); + }); + }); +}); From dd5ba352365de7d15dfc1a954cfe4a641c0bdd38 Mon Sep 17 00:00:00 2001 From: gabiwassan Date: Wed, 9 Jun 2021 16:25:23 -0300 Subject: [PATCH 13/24] test(error-orchestrator-ui): Added simple unit test to ErrorOrchestratorUi --- .../error-orchestrator-ui.test.ts | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 public/react-services/error-orchestrator/error-orchestrator-ui.test.ts diff --git a/public/react-services/error-orchestrator/error-orchestrator-ui.test.ts b/public/react-services/error-orchestrator/error-orchestrator-ui.test.ts new file mode 100644 index 0000000000..5a795a60f2 --- /dev/null +++ b/public/react-services/error-orchestrator/error-orchestrator-ui.test.ts @@ -0,0 +1,87 @@ +/* + * Wazuh app - Unit test for ErrorOrchestratorUI. + * + * Copyright (C) 2015-2021 Wazuh, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Find more information about this on the LICENSE file. + * + */ + +import { ErrorOrchestrator, UIErrorLog } from './types'; +import { ErrorOrchestratorUI } from './error-orchestrator-ui'; +import loglevel from 'loglevel'; + +const options: UIErrorLog = { + context: 'unitTest', + level: 'INFO', + severity: 'UI', + display: true, + store: false, + error: { + error: 'Testing loglevel INFO', + message: 'Message loglevel INFO', + title: 'title jest testing1', + }, +}; + +describe('Wazuh Error Orchestrator UI', () => { + describe('Given a valid options params for log INFO', () => { + it('Should be called loglevelInfo', () => { + const mockLoglevelInfo = loglevel.info = jest.fn(); + const mockError = 'Testing loglevel INFO'; + const mockMessage = 'Message loglevel INFO'; + + const errorOrchestratorUI: ErrorOrchestrator = new ErrorOrchestratorUI(); + errorOrchestratorUI.loadErrorLog(options); + + expect(mockLoglevelInfo).toBeCalled(); + expect(mockLoglevelInfo).toBeCalledWith(mockMessage, mockError); + expect(mockLoglevelInfo).toBeCalledTimes(1); + }); + }); + + describe('Given a valid options params for log WARNING', () => { + it('Should be called loglevelWarning', () => { + options.level = 'WARNING'; + options.error.error = 'Testing loglevel WARNING'; + options.error.message = 'Message loglevel WARNING'; + + const mockError = 'Testing loglevel WARNING'; + const mockMessage = 'Message loglevel WARNING'; + + const mockLoglevelWarning = loglevel.warn = jest.fn(); + + const errorOrchestratorUI: ErrorOrchestrator = new ErrorOrchestratorUI(); + errorOrchestratorUI.loadErrorLog(options); + + expect(mockLoglevelWarning).toBeCalled(); + expect(mockLoglevelWarning).toBeCalledWith(mockMessage, mockError); + expect(mockLoglevelWarning).toBeCalledTimes(1); + }); + }); + + describe('Given a valid options params for log ERROR', () => { + it('Should be called loglevelError', () => { + options.level = 'ERROR'; + options.error.error = 'Testing loglevel ERROR'; + options.error.message = 'Message loglevel ERROR'; + + const mockError = 'Testing loglevel ERROR'; + const mockMessage = 'Message loglevel ERROR'; + + const mockLoglevelError = loglevel.error = jest.fn(); + + const errorOrchestratorUI: ErrorOrchestrator = new ErrorOrchestratorUI(); + errorOrchestratorUI.loadErrorLog(options); + + expect(mockLoglevelError).toBeCalled(); + expect(mockLoglevelError).toBeCalledWith(mockMessage, mockError); + expect(mockLoglevelError).toBeCalledTimes(1); + }); + }); +}); From ba214d50e690b6c72daf172cf70637125130686a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Mart=C3=ADnez?= Date: Wed, 9 Jun 2021 21:37:37 +0200 Subject: [PATCH 14/24] Create new backend service (#3324) * Add endpoint * Create new backend service * Add changelog * Renamed constants * Added interfaces, created new controller and renamed * Created ui-logged, to prevent logger superclass * Added types, fixed responses types * Added new route file to ui-logs, changed method put to post, added in index,ts * Added test files, we must create all unit tests to those new features * Fixed if condition * Rename tests files, created endpoints test * Changed controller name ui-logs, removed duplicated export * Fixed file comments * Applied prettier formater * Added new base class base-logger * Remove wrong constants and fix errors * test(ui-logger-controller): Added simple unit test. * test(ui-logs-controller): Fix params. * Added test to ui-logs controller * Renamed test files * test(logs-controller): Added mock to function checkFileExist + prettier. * Solve comments * Add copyright and remove unused import Co-authored-by: Ibarra Maximiliano Co-authored-by: gabiwassan --- CHANGELOG.md | 4 + common/constants.ts | 8 +- server/controllers/index.ts | 2 +- server/controllers/wazuh-utils/index.ts | 2 + .../wazuh-utils/ui-logs.controller.test.ts | 64 ++++++ .../wazuh-utils/ui-logs.controller.ts | 96 ++++++++ .../{ => wazuh-utils}/wazuh-utils.ts | 16 +- server/lib/base-logger.ts | 214 ++++++++++++++++++ server/lib/logger.ts | 169 +------------- server/lib/ui-logger.ts | 18 ++ server/routes/index.ts | 3 +- server/routes/wazuh-utils/index.ts | 2 + server/routes/wazuh-utils/ui-logs.test.ts | 39 ++++ server/routes/wazuh-utils/ui-logs.ts | 39 ++++ .../{ => wazuh-utils}/wazuh-utils.test.ts | 0 .../routes/{ => wazuh-utils}/wazuh-utils.ts | 2 +- test/jest/config.js | 3 +- 17 files changed, 508 insertions(+), 173 deletions(-) create mode 100644 server/controllers/wazuh-utils/index.ts create mode 100644 server/controllers/wazuh-utils/ui-logs.controller.test.ts create mode 100644 server/controllers/wazuh-utils/ui-logs.controller.ts rename server/controllers/{ => wazuh-utils}/wazuh-utils.ts (89%) create mode 100644 server/lib/base-logger.ts create mode 100644 server/lib/ui-logger.ts create mode 100644 server/routes/wazuh-utils/index.ts create mode 100644 server/routes/wazuh-utils/ui-logs.test.ts create mode 100644 server/routes/wazuh-utils/ui-logs.ts rename server/routes/{ => wazuh-utils}/wazuh-utils.test.ts (100%) rename server/routes/{ => wazuh-utils}/wazuh-utils.ts (96%) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd3d7a548c..37f4d4fd2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to the Wazuh app project will be documented in this file. ## Wazuh v4.3.0 - Kibana 7.10.2 , 7.11.2 - Revision 4301 +### Added + +- Added new endpoint service to collect the frontend logs into a file [#3324](https://github.com/wazuh/wazuh-kibana-app/pull/3324) + ### Changed - Changed ossec to wazuh in sample-data [#3121](https://github.com/wazuh/wazuh-kibana-app/pull/3121) diff --git a/common/constants.ts b/common/constants.ts index 71b78dd6cb..56208aa173 100644 --- a/common/constants.ts +++ b/common/constants.ts @@ -125,8 +125,12 @@ export const WAZUH_DATA_CONFIG_REGISTRY_PATH = path.join(WAZUH_DATA_CONFIG_DIREC // Wazuh data path - logs export const WAZUH_DATA_LOGS_DIRECTORY_PATH = path.join(WAZUH_DATA_ABSOLUTE_PATH, 'logs'); -export const WAZUH_DATA_LOGS_PLAIN_PATH = path.join(WAZUH_DATA_LOGS_DIRECTORY_PATH, 'wazuhapp-plain.log'); -export const WAZUH_DATA_LOGS_RAW_PATH = path.join(WAZUH_DATA_LOGS_DIRECTORY_PATH, 'wazuhapp.log'); +export const WAZUH_DATA_LOGS_PLAIN_FILENAME = path.join(WAZUH_DATA_LOGS_DIRECTORY_PATH, 'wazuhapp-plain.log'); +export const WAZUH_DATA_LOGS_RAW_FILENAME = path.join(WAZUH_DATA_LOGS_DIRECTORY_PATH, 'wazuhapp.log'); + +// Wazuh data path - UI logs +export const WAZUH_UI_LOGS_PLAIN_FILENAME = path.join(WAZUH_DATA_LOGS_DIRECTORY_PATH, 'wazuh-ui-plain.log'); +export const WAZUH_UI_LOGS_RAW_FILENAME = path.join(WAZUH_DATA_LOGS_DIRECTORY_PATH, 'wazuh-ui.log'); // Wazuh data path - downloads export const WAZUH_DATA_DOWNLOADS_DIRECTORY_PATH = path.join(WAZUH_DATA_ABSOLUTE_PATH, 'downloads'); diff --git a/server/controllers/index.ts b/server/controllers/index.ts index 591a8f3fe4..c0f2597270 100644 --- a/server/controllers/index.ts +++ b/server/controllers/index.ts @@ -11,6 +11,6 @@ */ export { WazuhElasticCtrl } from './wazuh-elastic'; export { WazuhApiCtrl } from './wazuh-api'; -export { WazuhUtilsCtrl } from './wazuh-utils'; export { WazuhReportingCtrl } from './wazuh-reporting'; export { WazuhHostsCtrl } from './wazuh-hosts' +export * from './wazuh-utils'; \ No newline at end of file diff --git a/server/controllers/wazuh-utils/index.ts b/server/controllers/wazuh-utils/index.ts new file mode 100644 index 0000000000..ecac9686fe --- /dev/null +++ b/server/controllers/wazuh-utils/index.ts @@ -0,0 +1,2 @@ +export * from './wazuh-utils'; +export * from './ui-logs.controller'; diff --git a/server/controllers/wazuh-utils/ui-logs.controller.test.ts b/server/controllers/wazuh-utils/ui-logs.controller.test.ts new file mode 100644 index 0000000000..15eb657e6b --- /dev/null +++ b/server/controllers/wazuh-utils/ui-logs.controller.test.ts @@ -0,0 +1,64 @@ +import { UiLogsCtrl } from './ui-logs.controller'; +import { WAZUH_UI_LOGS_RAW_FILENAME } from '../../../common/constants'; +import uiLogger from '../../lib/ui-logger'; + +const readLastLines = require('read-last-lines'); + +const buildMockResponse = () => { + const res = {}; + res.ok = jest.fn().mockReturnValue(res); + return res; +}; + +const buildMockRequest = () => { + const req = {}; + req.body = jest.fn().mockReturnValue(req); + req.params = jest.fn().mockReturnValue(req); + return req; +}; + +describe('Spec UiLogsCtrl', function () { + describe('Check method getUiLogs ', () => { + it('Should 200 and return correct value', async () => { + const result = { body: { error: 0, rawLogs: ['my test mocked'] } }; + const mockResponse = buildMockResponse(); + jest.spyOn(readLastLines, 'read').mockReturnValue('my test mocked'); + jest.spyOn(uiLogger, 'checkFileExist').mockReturnValue(true); + + const controller = new UiLogsCtrl(); + await controller.getUiLogs(mockResponse); + + expect(mockResponse.ok).toHaveBeenCalledTimes(1); + expect(mockResponse.ok.mock.calls.length).toBe(1); + expect(mockResponse.ok).toHaveBeenCalledWith(result); + }); + + it('Should 200 and return message Log has been added', async () => { + const result = { body: { message: 'Log has been added' } }; + const mockResponse = buildMockResponse(); + jest.spyOn(readLastLines, 'read').mockReturnValue('Log has been added'); + jest.spyOn(uiLogger, 'checkFileExist').mockReturnValue(true); + + const mockRequest = buildMockRequest(); + mockRequest.body = { + level: 'error', + message: 'Message example', + location: 'Location example', + }; + + const controller = new UiLogsCtrl(); + await controller.createUiLogs(mockRequest, mockResponse); + + expect(mockResponse.ok).toHaveBeenCalledTimes(1); + expect(mockResponse.ok.mock.calls.length).toBe(1); + expect(mockResponse.ok).toHaveBeenCalledWith(result); + }); + + it('Should return a Array logs', async () => { + const controller = new UiLogsCtrl(); + let res = await controller.getUiFileLogs(WAZUH_UI_LOGS_RAW_FILENAME); + + expect(Array.isArray(res)).toBe(true); + }); + }); +}); diff --git a/server/controllers/wazuh-utils/ui-logs.controller.ts b/server/controllers/wazuh-utils/ui-logs.controller.ts new file mode 100644 index 0000000000..6080e6672f --- /dev/null +++ b/server/controllers/wazuh-utils/ui-logs.controller.ts @@ -0,0 +1,96 @@ +/* + * Wazuh app - Class for UI Logs functions + * Copyright (C) 2015-2021 Wazuh, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Find more information about this on the LICENSE file. + */ + +// Require some libraries +import { ErrorResponse } from '../../lib/error-response'; +import { read } from 'read-last-lines'; +import { WAZUH_UI_LOGS_RAW_FILENAME } from '../../../common/constants'; +import { KibanaRequest, RequestHandlerContext, KibanaResponseFactory } from 'src/core/server'; +import uiLogger from '../../lib/ui-logger'; + +export class UiLogsCtrl { + /** + * Constructor + * @param {*} server + */ + constructor() {} + + /** + * Returns Wazuh ui logs + * @param {Object} response + * @returns {Array} app logs or ErrorResponse + */ + async getUiLogs( + response: KibanaResponseFactory + ) { + try { + return uiLogger.initDirectory().then(async () => { + if (!uiLogger.checkFileExist(WAZUH_UI_LOGS_RAW_FILENAME)) { + return response.ok({ + body: { + error: 0, + rawLogs: [], + }, + }); + } else { + let arrayLog = await this.getUiFileLogs(WAZUH_UI_LOGS_RAW_FILENAME); + return response.ok({ + body: { + error: 0, + rawLogs: arrayLog.filter((item) => typeof item === 'string' && item.length), + }, + }); + } + }); + } catch (error) { + return ErrorResponse(error.message || error, 3036, 500, response); + } + } + + /** + * Add new UI Log entry in ui logs file + * @param context + * @param request + * @param response + * @returns success message or ErrorResponse + */ + async createUiLogs( + request: KibanaRequest, + response: KibanaResponseFactory + ) { + try { + const { location, message, level } = request.body; + await uiLogger.log(location, message, level); + return response.ok({ + body: { + message: 'Log has been added', + }, + }); + } catch (error) { + return ErrorResponse(error.message || error, 3021, 500, response); + } + } + + /** + * Get UI logs from specific log file + * @param filepath + * @returns Array + */ + async getUiFileLogs(filepath) { + try { + const lastLogs = await read(filepath, 50); + return lastLogs.split('\n'); + } catch (err) { + throw err; + } + } +} diff --git a/server/controllers/wazuh-utils.ts b/server/controllers/wazuh-utils/wazuh-utils.ts similarity index 89% rename from server/controllers/wazuh-utils.ts rename to server/controllers/wazuh-utils/wazuh-utils.ts index 466923d44b..97567ec7ad 100644 --- a/server/controllers/wazuh-utils.ts +++ b/server/controllers/wazuh-utils/wazuh-utils.ts @@ -11,15 +11,15 @@ */ // Require some libraries -import { ErrorResponse } from '../lib/error-response'; -import { getConfiguration } from '../lib/get-configuration'; +import { ErrorResponse } from '../../lib/error-response'; +import { getConfiguration } from '../../lib/get-configuration'; import { read } from 'read-last-lines'; -import { UpdateConfigurationFile } from '../lib/update-configuration'; +import { UpdateConfigurationFile } from '../../lib/update-configuration'; import jwtDecode from 'jwt-decode'; -import { WAZUH_ROLE_ADMINISTRATOR_ID, WAZUH_DATA_LOGS_RAW_PATH } from '../../common/constants'; -import { ManageHosts } from '../lib/manage-hosts'; +import { WAZUH_ROLE_ADMINISTRATOR_ID, WAZUH_DATA_LOGS_RAW_FILENAME, WAZUH_UI_LOGS_RAW_PATH } from '../../../common/constants'; +import { ManageHosts } from '../../lib/manage-hosts'; import { KibanaRequest, RequestHandlerContext, KibanaResponseFactory } from 'src/core/server'; -import { getCookieValueByName } from '../lib/cookie'; +import { getCookieValueByName } from '../../lib/cookie'; const updateConfigurationFile = new UpdateConfigurationFile(); @@ -108,7 +108,7 @@ export class WazuhUtilsCtrl { async getAppLogs(context: RequestHandlerContext, request: KibanaRequest, response: KibanaResponseFactory) { try { const lastLogs = await read( - WAZUH_DATA_LOGS_RAW_PATH, + WAZUH_DATA_LOGS_RAW_FILENAME, 50 ); const spliterLog = lastLogs.split('\n'); @@ -126,4 +126,6 @@ export class WazuhUtilsCtrl { return ErrorResponse(error.message || error, 3036, 500, response); } } + + } diff --git a/server/lib/base-logger.ts b/server/lib/base-logger.ts new file mode 100644 index 0000000000..17b0cf71dd --- /dev/null +++ b/server/lib/base-logger.ts @@ -0,0 +1,214 @@ +/* + * Wazuh app - Settings controller + * Copyright (C) 2015-2021 Wazuh, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Find more information about this on the LICENSE file. + */ + +import winston from 'winston'; +import fs from 'fs'; +import path from 'path'; +import { getConfiguration } from './get-configuration'; +import { createDataDirectoryIfNotExists } from './filesystem'; + +import { WAZUH_DATA_LOGS_DIRECTORY_PATH } from '../../common/constants'; + +export interface IUIPlainLoggerSettings { + level: string; + message: string; + } + + export interface IUILoggerSettings extends IUIPlainLoggerSettings { + date: Date; + location: string; + } + +export class BaseLogger { + allowed: boolean = false; + wazuhLogger: winston.Logger | undefined = undefined; + wazuhPlainLogger: winston.Logger | undefined = undefined; + PLAIN_LOGS_PATH: string = ''; + RAW_LOGS_PATH: string = ''; + + constructor(plainLogsPath: string, rawLogsPath: string) { + this.PLAIN_LOGS_PATH = path.join(WAZUH_DATA_LOGS_DIRECTORY_PATH, plainLogsPath); + this.RAW_LOGS_PATH = path.join(WAZUH_DATA_LOGS_DIRECTORY_PATH, rawLogsPath); + } + + /** + * Initialize loggers, plain and raw logger + */ + private initLogger = () => { + const configurationFile = getConfiguration(); + const level = + typeof (configurationFile || {})['logs.level'] !== 'undefined' && + ['info', 'debug'].includes(configurationFile['logs.level']) + ? configurationFile['logs.level'] + : 'info'; + + // JSON logger + this.wazuhLogger = winston.createLogger({ + level, + format: winston.format.json(), + transports: [ + new winston.transports.File({ + filename: this.RAW_LOGS_PATH, + }), + ], + }); + + // Prevents from exit on error related to the logger. + this.wazuhLogger.exitOnError = false; + + // Plain text logger + this.wazuhPlainLogger = winston.createLogger({ + level, + format: winston.format.simple(), + transports: [ + new winston.transports.File({ + filename: this.PLAIN_LOGS_PATH, + }), + ], + }); + + // Prevents from exit on error related to the logger. + this.wazuhPlainLogger.exitOnError = false; + }; + + /** + * Checks if wazuh/logs exists. If it doesn't exist, it will be created. + */ + initDirectory = async () => { + try { + createDataDirectoryIfNotExists(); + createDataDirectoryIfNotExists('logs'); + if (typeof this.wazuhLogger === 'undefined' || typeof this.wazuhPlainLogger === 'undefined') { + this.initLogger(); + } + this.allowed = true; + return; + } catch (error) { + this.allowed = false; + return Promise.reject(error); + } + }; + + /** + * Returns given file size in MB, if the file doesn't exist returns 0 + * @param {*} filename Path to the file + */ + getFilesizeInMegaBytes = (filename) => { + if (this.allowed) { + if (fs.existsSync(filename)) { + const stats = fs.statSync(filename); + const fileSizeInMegaBytes = stats.size; + + return fileSizeInMegaBytes / 1000000.0; + } + } + return 0; + }; + + /** + * Check if file exist + * @param filename + * @returns boolean + */ + checkFileExist = (filename) => { + return fs.existsSync(filename); + }; + + /** + * Checks if the wazuhapp.log file size is greater than 100MB, if so it rotates the file. + */ + private checkFiles = () => { + if (this.allowed) { + // check raw log file + if (this.getFilesizeInMegaBytes(this.RAW_LOGS_PATH) >= 100) { + fs.renameSync( + this.RAW_LOGS_PATH, + `${WAZUH_DATA_LOGS_DIRECTORY_PATH}/wazuhapp.${new Date().getTime()}.log` + ); + fs.writeFileSync( + this.RAW_LOGS_PATH, + JSON.stringify({ + date: new Date(), + level: 'info', + location: 'logger', + message: 'Rotated log file', + }) + '\n' + ); + } + + // check log file + if (this.getFilesizeInMegaBytes(this.PLAIN_LOGS_PATH) >= 100) { + fs.renameSync( + this.PLAIN_LOGS_PATH, + `${WAZUH_DATA_LOGS_DIRECTORY_PATH}/wazuhapp-plain.${new Date().getTime()}.log` + ); + } + } + }; + + + /** + * Get Current Date + * @returns string + */ + private yyyymmdd = () => { + const now = new Date(); + const y = now.getFullYear(); + const m = now.getMonth() + 1; + const d = now.getDate(); + const seconds = now.getSeconds(); + const minutes = now.getMinutes(); + const hour = now.getHours(); + return `${y}/${m < 10 ? '0' : ''}${m}/${d < 10 ? '0' : ''}${d} ${hour}:${minutes}:${seconds}`; + }; + + + + /** + * Main function to add a new log + * @param {*} location File where the log is being thrown + * @param {*} message Message to show + * @param {*} level Optional, default is 'error' + */ + + async log(location: string, message: string, level: string) { + return this.initDirectory() + .then(() => { + if (this.allowed) { + this.checkFiles(); + + const plainLogData: IUIPlainLoggerSettings = { + level: level || 'error', + message: `${this.yyyymmdd()}: ${location || 'Unknown origin'}: ${ + message || 'An error occurred' + }`, + }; + + this.wazuhPlainLogger.log(plainLogData); + + const logData: IUILoggerSettings = { + date: new Date(), + level: level || 'error', + location: location || 'Unknown origin', + message: message || 'An error occurred', + }; + + this.wazuhLogger.log(logData); + } + }) + .catch((error) => { + console.error(`Cannot create the logs directory due to:\n${error.message || error}`); + throw error; + }); + + } +} diff --git a/server/lib/logger.ts b/server/lib/logger.ts index 7cb8115d79..435edb5d88 100644 --- a/server/lib/logger.ts +++ b/server/lib/logger.ts @@ -9,165 +9,14 @@ * * Find more information about this on the LICENSE file. */ -import winston from 'winston'; -import fs from 'fs'; -import { getConfiguration } from './get-configuration'; -import { WAZUH_DATA_LOGS_DIRECTORY_PATH, WAZUH_DATA_LOGS_PLAIN_PATH, WAZUH_DATA_LOGS_RAW_PATH } from '../../common/constants'; -import { createDataDirectoryIfNotExists } from './filesystem'; +import { BaseLogger } from './base-logger'; +import { + WAZUH_DATA_LOGS_PLAIN_FILENAME, + WAZUH_DATA_LOGS_RAW_FILENAME +} from '../../common/constants'; -let allowed = false; -let wazuhlogger = undefined; -let wazuhPlainLogger = undefined; +const logger = new BaseLogger(WAZUH_DATA_LOGS_PLAIN_FILENAME,WAZUH_DATA_LOGS_RAW_FILENAME); -/** - * Here we create the loggers - */ -const initLogger = () => { - const configurationFile = getConfiguration(); - const level = - typeof (configurationFile || {})['logs.level'] !== 'undefined' && - ['info', 'debug'].includes(configurationFile['logs.level']) - ? configurationFile['logs.level'] - : 'info'; - - // JSON logger - wazuhlogger = winston.createLogger({ - level, - format: winston.format.json(), - transports: [ - new winston.transports.File({ - filename: WAZUH_DATA_LOGS_RAW_PATH - }) - ] - }); - - // Prevents from exit on error related to the logger. - wazuhlogger.exitOnError = false; - - // Plain text logger - wazuhPlainLogger = winston.createLogger({ - level, - format: winston.format.simple(), - transports: [ - new winston.transports.File({ - filename: WAZUH_DATA_LOGS_PLAIN_PATH - }) - ] - }); - - // Prevents from exit on error related to the logger. - wazuhPlainLogger.exitOnError = false; -}; - -/** - * Checks if wazuh/logs exists. If it doesn't exist, it will be created. - */ -const initDirectory = async () => { - try { - createDataDirectoryIfNotExists(); - createDataDirectoryIfNotExists('logs'); - if ( - typeof wazuhlogger === 'undefined' || - typeof wazuhPlainLogger === 'undefined' - ) { - initLogger(); - } - allowed = true; - return; - } catch (error) { - allowed = false; - return Promise.reject(error); - } -}; - -/** - * Returns given file size in MB, if the file doesn't exist returns 0 - * @param {*} filename Path to the file - */ -const getFilesizeInMegaBytes = filename => { - if (allowed) { - if (fs.existsSync(filename)) { - const stats = fs.statSync(filename); - const fileSizeInMegaBytes = stats.size; - - return fileSizeInMegaBytes / 1000000.0; - } - } - return 0; -}; - -/** - * Checks if the wazuhapp.log file size is greater than 100MB, if so it rotates the file. - */ -const checkFiles = () => { - if (allowed) { - if (getFilesizeInMegaBytes(WAZUH_DATA_LOGS_RAW_PATH) >= 100) { - fs.renameSync( - WAZUH_DATA_LOGS_RAW_PATH, - `${WAZUH_DATA_LOGS_DIRECTORY_PATH}/wazuhapp.${new Date().getTime()}.log` - ); - fs.writeFileSync( - WAZUH_DATA_LOGS_RAW_PATH, - JSON.stringify({ - date: new Date(), - level: 'info', - location: 'logger', - message: 'Rotated log file' - }) + '\n' - ); - } - if (getFilesizeInMegaBytes(WAZUH_DATA_LOGS_PLAIN_PATH) >= 100) { - fs.renameSync( - WAZUH_DATA_LOGS_PLAIN_PATH, - `${WAZUH_DATA_LOGS_DIRECTORY_PATH}/wazuhapp-plain.${new Date().getTime()}.log` - ); - } - } -}; - -const yyyymmdd = () => { - const now = new Date(); - const y = now.getFullYear(); - const m = now.getMonth() + 1; - const d = now.getDate(); - const seconds = now.getSeconds(); - const minutes = now.getMinutes(); - const hour = now.getHours(); - return `${y}/${m < 10 ? '0' : ''}${m}/${ - d < 10 ? '0' : '' - }${d} ${hour}:${minutes}:${seconds}`; -}; - -/** - * Main function to add a new log - * @param {*} location File where the log is being thrown - * @param {*} message Message to show - * @param {*} level Optional, default is 'error' - */ -export function log(location, message, level) { - initDirectory() - .then(() => { - if (allowed) { - checkFiles(); - wazuhlogger.log({ - date: new Date(), - level: level || 'error', - location: location || 'Unknown origin', - message: message || 'An error occurred' - }); - try { - wazuhPlainLogger.log({ - level: level || 'error', - message: `${yyyymmdd()}: ${location || - 'Unknown origin'}: ${message || 'An error occurred'}` - }); - } catch (error) {} // eslint-disable-line - } - }) - .catch(error => - // eslint-disable-next-line - console.error( - `Cannot create the logs directory due to:\n${error.message || error}` - ) - ); -} +export const log = (location, message, level) => { + logger.log(location, message, level ) +} \ No newline at end of file diff --git a/server/lib/ui-logger.ts b/server/lib/ui-logger.ts new file mode 100644 index 0000000000..2ac71076e9 --- /dev/null +++ b/server/lib/ui-logger.ts @@ -0,0 +1,18 @@ +/* + * Wazuh app - Module for ui logging functions + * Copyright (C) 2015-2021 Wazuh, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Find more information about this on the LICENSE file. + */ +import { BaseLogger } from './base-logger'; +import { + WAZUH_UI_LOGS_PLAIN_FILENAME, + WAZUH_UI_LOGS_RAW_FILENAME +} from '../../common/constants'; + +export default new BaseLogger(WAZUH_UI_LOGS_PLAIN_FILENAME,WAZUH_UI_LOGS_RAW_FILENAME); diff --git a/server/routes/index.ts b/server/routes/index.ts index 5f9b743be0..8af0e65dc0 100644 --- a/server/routes/index.ts +++ b/server/routes/index.ts @@ -2,7 +2,7 @@ import { IRouter } from 'kibana/server'; import { WazuhApiRoutes } from './wazuh-api'; import { WazuhElasticRoutes } from "./wazuh-elastic"; import { WazuhHostsRoutes } from "./wazuh-hosts"; -import { WazuhUtilsRoutes } from "./wazuh-utils"; +import { WazuhUtilsRoutes, UiLogsRoutes } from './wazuh-utils' import { WazuhReportingRoutes } from "./wazuh-reporting"; export const setupRoutes = (router: IRouter) => { @@ -11,4 +11,5 @@ export const setupRoutes = (router: IRouter) => { WazuhHostsRoutes(router); WazuhUtilsRoutes(router); WazuhReportingRoutes(router); + UiLogsRoutes(router); }; diff --git a/server/routes/wazuh-utils/index.ts b/server/routes/wazuh-utils/index.ts new file mode 100644 index 0000000000..f02845ce09 --- /dev/null +++ b/server/routes/wazuh-utils/index.ts @@ -0,0 +1,2 @@ +export { WazuhUtilsRoutes } from "./wazuh-utils"; +export { UiLogsRoutes } from './ui-logs'; \ No newline at end of file diff --git a/server/routes/wazuh-utils/ui-logs.test.ts b/server/routes/wazuh-utils/ui-logs.test.ts new file mode 100644 index 0000000000..a6fcebc80e --- /dev/null +++ b/server/routes/wazuh-utils/ui-logs.test.ts @@ -0,0 +1,39 @@ +// To launch this file +// yarn test:jest --testEnvironment node --verbose server/routes/wazuh-utils/ui-logs +import axios from 'axios'; + +function buildAxiosOptions(method: string, path: string, data: any = {}, headers: any = {}){ + return { + method: method, + headers: { 'Content-Type': 'application/json', 'kbn-xsrf': 'kibana', ...headers }, + url: `http://localhost:5601${path}`, + data: data + }; +}; + + +describe('Wazuh API - /utils/logs/ui', () => { + test('[200] Get UI Logs', () => { + const options = buildAxiosOptions('get', '/utils/logs/ui'); + return axios(options).then(response => { + expect(response.status).toBe(200); + //expect(typeof response.data.data).toBe('object'); + //expect(typeof response.data.data.hosts).toBe('object'); + }).catch(error => {throw error}) + },6000); +}); + +describe('Wazuh API - /utils/logs/ui', () => { + let userToken = null; + + test('[200] Create UI Logs', () => { + const options = buildAxiosOptions('post', '/utils/logs/ui', { + message: 'Message test', + level: 'error', + location: 'Location' + }); + return axios(options).then(response => { + expect(response.status).toBe(200); + }).catch(error => {throw error}) + },6000); +}); diff --git a/server/routes/wazuh-utils/ui-logs.ts b/server/routes/wazuh-utils/ui-logs.ts new file mode 100644 index 0000000000..345f16af6f --- /dev/null +++ b/server/routes/wazuh-utils/ui-logs.ts @@ -0,0 +1,39 @@ +/* + * Wazuh app - Module for UI Logs routes + * Copyright (C) 2015-2021 Wazuh, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Find more information about this on the LICENSE file. + */ +import { UiLogsCtrl } from '../../controllers'; +import { IRouter } from 'kibana/server'; +import { schema } from '@kbn/config-schema'; + +export function UiLogsRoutes(router: IRouter) { + const ctrl = new UiLogsCtrl(); + router.get( + { + path: '/utils/logs/ui', + validate: false, + }, + async (context, request, response) => await ctrl.getUiLogs(context, request, response) + ); + + router.post( + { + path: '/utils/logs/ui', + validate: { + body: schema.object({ + message: schema.string(), + level: schema.string(), + location: schema.string(), + }), + }, + }, + async (context, request, response) => await ctrl.createUiLogs(context, request, response) + ); +} diff --git a/server/routes/wazuh-utils.test.ts b/server/routes/wazuh-utils/wazuh-utils.test.ts similarity index 100% rename from server/routes/wazuh-utils.test.ts rename to server/routes/wazuh-utils/wazuh-utils.test.ts diff --git a/server/routes/wazuh-utils.ts b/server/routes/wazuh-utils/wazuh-utils.ts similarity index 96% rename from server/routes/wazuh-utils.ts rename to server/routes/wazuh-utils/wazuh-utils.ts index f1d6608f51..52587dd0d3 100644 --- a/server/routes/wazuh-utils.ts +++ b/server/routes/wazuh-utils/wazuh-utils.ts @@ -9,7 +9,7 @@ * * Find more information about this on the LICENSE file. */ -import { WazuhUtilsCtrl } from '../controllers'; +import { WazuhUtilsCtrl } from '../../controllers'; import { IRouter } from 'kibana/server'; import { schema } from '@kbn/config-schema'; diff --git a/test/jest/config.js b/test/jest/config.js index 3e6e911ff9..4a1fe3f72a 100644 --- a/test/jest/config.js +++ b/test/jest/config.js @@ -48,7 +48,8 @@ export default { 'target/', ], testMatch: [ - '**/*.test.{js,ts,tsx}' + '**/*.test.{js,ts,tsx}', + '**/*{js,ts,tsx}' ], transform: { '^.+\\.js$': `${kbnDir}/src/dev/jest/babel_transform.js`, From dc8a304da7e3d5e591b5086c68ede1db9e2fc5be Mon Sep 17 00:00:00 2001 From: gabiwassan Date: Thu, 10 Jun 2021 17:29:23 -0300 Subject: [PATCH 15/24] bugfix(error-orchestrator): Added some improvements and fixes. --- .../error-orchestrator/error-orchestrator-base.ts | 8 +++----- .../error-orchestrator-business.ts | 2 ++ .../controllers/wazuh-utils/ui-logs.controller.ts | 14 +++++--------- server/routes/wazuh-utils/ui-logs.ts | 4 ++-- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/public/react-services/error-orchestrator/error-orchestrator-base.ts b/public/react-services/error-orchestrator/error-orchestrator-base.ts index 61592d8ba9..dc585ec3ec 100644 --- a/public/react-services/error-orchestrator/error-orchestrator-base.ts +++ b/public/react-services/error-orchestrator/error-orchestrator-base.ts @@ -27,11 +27,9 @@ export class ErrorOrchestratorBase implements ErrorOrchestrator { private async storeError(errorLog: UIErrorLog) { try { await GenericRequest.request('POST', `/utils/logs/ui`, { - body: { - message: errorLog.error.message, - level: errorLog.level, - location: errorLog.location, - }, + message: errorLog.error.message, + level: errorLog.level, + location: errorLog.location, }); } catch (error) { loglevel.error('Failed on request [POST /utils/logs/ui]', error); diff --git a/public/react-services/error-orchestrator/error-orchestrator-business.ts b/public/react-services/error-orchestrator/error-orchestrator-business.ts index 851bb77b96..13ecf0c326 100644 --- a/public/react-services/error-orchestrator/error-orchestrator-business.ts +++ b/public/react-services/error-orchestrator/error-orchestrator-business.ts @@ -19,8 +19,10 @@ import { ErrorToastOptions } from 'kibana/public'; export class ErrorOrchestratorBusiness extends ErrorOrchestratorBase { public displayError(errorLog: UIErrorLog) { const toast = { + error: errorLog.error, title: errorLog.error.title, toastMessage: errorLog.error.message, + message: errorLog.error.message, toastLifeTimeMs: 3000, }; diff --git a/server/controllers/wazuh-utils/ui-logs.controller.ts b/server/controllers/wazuh-utils/ui-logs.controller.ts index 6080e6672f..f2c7cc184d 100644 --- a/server/controllers/wazuh-utils/ui-logs.controller.ts +++ b/server/controllers/wazuh-utils/ui-logs.controller.ts @@ -14,7 +14,7 @@ import { ErrorResponse } from '../../lib/error-response'; import { read } from 'read-last-lines'; import { WAZUH_UI_LOGS_RAW_FILENAME } from '../../../common/constants'; -import { KibanaRequest, RequestHandlerContext, KibanaResponseFactory } from 'src/core/server'; +import { KibanaRequest, KibanaResponseFactory } from 'src/core/server'; import uiLogger from '../../lib/ui-logger'; export class UiLogsCtrl { @@ -29,9 +29,7 @@ export class UiLogsCtrl { * @param {Object} response * @returns {Array} app logs or ErrorResponse */ - async getUiLogs( - response: KibanaResponseFactory - ) { + async getUiLogs(response: KibanaResponseFactory) { try { return uiLogger.initDirectory().then(async () => { if (!uiLogger.checkFileExist(WAZUH_UI_LOGS_RAW_FILENAME)) { @@ -58,20 +56,18 @@ export class UiLogsCtrl { /** * Add new UI Log entry in ui logs file - * @param context * @param request * @param response * @returns success message or ErrorResponse */ - async createUiLogs( - request: KibanaRequest, - response: KibanaResponseFactory - ) { + async createUiLogs(request: KibanaRequest, response: KibanaResponseFactory) { try { const { location, message, level } = request.body; await uiLogger.log(location, message, level); return response.ok({ body: { + statusCode: 200, + error: 0, message: 'Log has been added', }, }); diff --git a/server/routes/wazuh-utils/ui-logs.ts b/server/routes/wazuh-utils/ui-logs.ts index 345f16af6f..5d4b7a5844 100644 --- a/server/routes/wazuh-utils/ui-logs.ts +++ b/server/routes/wazuh-utils/ui-logs.ts @@ -20,7 +20,7 @@ export function UiLogsRoutes(router: IRouter) { path: '/utils/logs/ui', validate: false, }, - async (context, request, response) => await ctrl.getUiLogs(context, request, response) + async (context, request, response) => await ctrl.getUiLogs(response) ); router.post( @@ -34,6 +34,6 @@ export function UiLogsRoutes(router: IRouter) { }), }, }, - async (context, request, response) => await ctrl.createUiLogs(context, request, response) + async (context, request, response) => await ctrl.createUiLogs(request, response) ); } From 3ba96f4e15b74fa19207e0c817933a09d65179e1 Mon Sep 17 00:00:00 2001 From: gabiwassan Date: Thu, 10 Jun 2021 17:49:56 -0300 Subject: [PATCH 16/24] test(ui-logs-controller): Updated unit test. --- server/controllers/wazuh-utils/ui-logs.controller.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/controllers/wazuh-utils/ui-logs.controller.test.ts b/server/controllers/wazuh-utils/ui-logs.controller.test.ts index 15eb657e6b..5553f4bef1 100644 --- a/server/controllers/wazuh-utils/ui-logs.controller.test.ts +++ b/server/controllers/wazuh-utils/ui-logs.controller.test.ts @@ -34,7 +34,7 @@ describe('Spec UiLogsCtrl', function () { }); it('Should 200 and return message Log has been added', async () => { - const result = { body: { message: 'Log has been added' } }; + const result = { body: { error: 0, message: 'Log has been added', statusCode: 200 } }; const mockResponse = buildMockResponse(); jest.spyOn(readLastLines, 'read').mockReturnValue('Log has been added'); jest.spyOn(uiLogger, 'checkFileExist').mockReturnValue(true); From ec219f56376d846b7d9615bf6271533748e0abb7 Mon Sep 17 00:00:00 2001 From: pablomarga Date: Fri, 11 Jun 2021 12:27:43 +0200 Subject: [PATCH 17/24] Settings --- .../add-modules-data/WzSampleDataWrapper.js | 16 +++++----- .../cluster/cluster-visualization.js | 31 +++++++------------ public/components/notifications/modal.tsx | 5 +-- public/components/settings/api/add-api.js | 5 +-- public/components/settings/api/api-is-down.js | 5 +-- public/components/settings/api/api-table.js | 10 +++--- .../settings/configuration/configuration.tsx | 17 +++++----- public/components/settings/modules/modules.js | 16 +++++----- .../components/settings/settings-logs/logs.js | 7 ++++- .../agent/components/agents-preview.js | 3 +- .../agent/components/register-agent.js | 9 ++---- 11 files changed, 57 insertions(+), 67 deletions(-) diff --git a/public/components/add-modules-data/WzSampleDataWrapper.js b/public/components/add-modules-data/WzSampleDataWrapper.js index 6fd6d81996..d3c6331863 100644 --- a/public/components/add-modules-data/WzSampleDataWrapper.js +++ b/public/components/add-modules-data/WzSampleDataWrapper.js @@ -24,8 +24,9 @@ import { } from '@elastic/eui'; import WzSampleData from './sample-data' import WzReduxProvider from '../../redux/wz-redux-provider'; -import { withUserAuthorizationPrompt } from '../../components/common/hocs/withUserAuthorization'; +import { withUserAuthorizationPrompt, withErrorBoundary, withReduxProvider } from '../../components/common/hocs'; import store from '../../redux/store'; +import { compose } from 'redux'; import { updateSelectedSettingsSection } from '../../redux/actions/appStateActions'; import { WAZUH_ROLE_ADMINISTRATOR_NAME } from '../../../common/constants'; @@ -73,11 +74,8 @@ export class WzSampleDataProvider extends Component { } } -const WzSampleDataWrapperWithAdministrator = withUserAuthorizationPrompt(null, [WAZUH_ROLE_ADMINISTRATOR_NAME])(WzSampleDataProvider); -export function WzSampleDataWrapper() { - return ( - - - - ) -} +export const WzSampleDataWrapper = compose( + withErrorBoundary, + withReduxProvider, + withUserAuthorizationPrompt(null, [WAZUH_ROLE_ADMINISTRATOR_NAME]) +)(WzSampleDataProvider); diff --git a/public/components/management/cluster/cluster-visualization.js b/public/components/management/cluster/cluster-visualization.js index e80377a1dd..cf698b57cc 100644 --- a/public/components/management/cluster/cluster-visualization.js +++ b/public/components/management/cluster/cluster-visualization.js @@ -1,23 +1,14 @@ -import React, { Component } from 'react'; -import WzReduxProvider from '../../../redux/wz-redux-provider'; +import React from 'react'; import KibanaVis from '../../../kibana-integrations/kibana-vis'; +import { withErrorBoundary, withReduxProvider } from '../../../components/common/hocs'; +import {compose} from 'redux' -export class KibanaVisWrapper extends Component { - constructor(props) { - super(props); - this.state = {}; - } - - - render() { - return ( -
- - - -
- ); - } +function KibanaVisClass() { + return ( +
+ +
+ ); } + +export const KibanaVisWrapper = compose(withErrorBoundary, withReduxProvider)(KibanaVisClass); \ No newline at end of file diff --git a/public/components/notifications/modal.tsx b/public/components/notifications/modal.tsx index 2bcaccf172..1680613acb 100644 --- a/public/components/notifications/modal.tsx +++ b/public/components/notifications/modal.tsx @@ -27,9 +27,10 @@ import { import { useSelector, useDispatch } from 'react-redux'; import { updateToastNotificationsModal } from '../../redux/actions/appStateActions'; -import { withReduxProvider } from '../common/hocs' +import { withReduxProvider, withErrorBoundary } from '../common/hocs'; +import { compose } from 'redux'; -export const ToastNotificationsModal = withReduxProvider(() => { +export const ToastNotificationsModal = compose (withErrorBoundary, withReduxProvider)(() => { const [isOpen, setIsOpen] = useState(false); const toastNotification = useSelector(state => state.appStateReducers.toastNotification); const dispatch = useDispatch(); diff --git a/public/components/settings/api/add-api.js b/public/components/settings/api/add-api.js index e7ffe9b70d..e10e8bf5d0 100644 --- a/public/components/settings/api/add-api.js +++ b/public/components/settings/api/add-api.js @@ -25,8 +25,9 @@ import { EuiCallOut, EuiPanel } from '@elastic/eui'; +import { withErrorBoundary } from '../../common/hocs'; -export class AddApi extends Component { +export const AddApi = withErrorBoundary (class AddApi extends Component { constructor(props) { super(props); this.state = { @@ -203,7 +204,7 @@ export class AddApi extends Component { return view; } -} +}) AddApi.propTypes = { checkForNewApis: PropTypes.func, diff --git a/public/components/settings/api/api-is-down.js b/public/components/settings/api/api-is-down.js index 16eb39ba3e..c81f9f87e5 100644 --- a/public/components/settings/api/api-is-down.js +++ b/public/components/settings/api/api-is-down.js @@ -30,8 +30,9 @@ import { EuiButtonIcon, EuiPanel } from '@elastic/eui'; +import { withErrorBoundary } from '../../common/hocs'; -export class ApiIsDown extends Component { +export const ApiIsDown = withErrorBoundary (class ApiIsDown extends Component { constructor(props) { super(props); this.state = { @@ -257,7 +258,7 @@ hosts: ); } -} +}); ApiIsDown.propTypes = { apiEntries: PropTypes.array, diff --git a/public/components/settings/api/api-table.js b/public/components/settings/api/api-table.js index fe3cec4133..3bdab8d65e 100644 --- a/public/components/settings/api/api-table.js +++ b/public/components/settings/api/api-table.js @@ -28,13 +28,15 @@ import { EuiIcon } from '@elastic/eui'; import { WzButtonPermissions } from '../../common/permissions/button'; -import WzReduxProvider from '../../../redux/wz-redux-provider'; import store from '../../../redux/store'; import { updateSelectedSettingsSection } from '../../../redux/actions/appStateActions'; import { AppState } from '../../../react-services/app-state'; import { API_USER_STATUS_RUN_AS } from '../../../../server/lib/cache-api-user-has-run-as'; +import { withErrorBoundary, withReduxProvider } from '../../common/hocs'; +import { compose } from 'redux' -export class ApiTable extends Component { + +export const ApiTable = compose(withErrorBoundary, withReduxProvider)(class ApiTable extends Component { constructor(props) { super(props); @@ -295,7 +297,6 @@ export class ApiTable extends Component { return ( - @@ -344,11 +345,10 @@ export class ApiTable extends Component { loading={this.state.refreshingEntries} /> - ); } -} +}); ApiTable.propTypes = { apiEntries: PropTypes.array, diff --git a/public/components/settings/configuration/configuration.tsx b/public/components/settings/configuration/configuration.tsx index 62e7dc75bb..bc8a712fda 100644 --- a/public/components/settings/configuration/configuration.tsx +++ b/public/components/settings/configuration/configuration.tsx @@ -27,11 +27,11 @@ import { categoriesEquivalence, formEquivalence } from '../../../utils/config-equivalences'; -import WzReduxProvider from '../../../redux/wz-redux-provider' import store from '../../../redux/store' import { updateSelectedSettingsSection } from '../../../redux/actions/appStateActions'; -import { withUserAuthorizationPrompt } from '../../common/hocs/withUserAuthorization' +import { withUserAuthorizationPrompt, withErrorBoundary, withReduxProvider } from '../../common/hocs' import { WAZUH_ROLE_ADMINISTRATOR_NAME } from '../../../../common/constants'; +import { compose } from 'redux'; export type ISetting = { setting: string @@ -81,11 +81,8 @@ const WzConfigurationSettingsProvider = (props) => {
); } -const WzConfigurationSettingsWrapper = withUserAuthorizationPrompt(null, [WAZUH_ROLE_ADMINISTRATOR_NAME])(WzConfigurationSettingsProvider); -export function WzConfigurationSettings(props) { - return( - - - - ); -} +export const WzConfigurationSettings = compose ( + withErrorBoundary, + withReduxProvider, + withUserAuthorizationPrompt(null, [WAZUH_ROLE_ADMINISTRATOR_NAME]) +)(WzConfigurationSettingsProvider); diff --git a/public/components/settings/modules/modules.js b/public/components/settings/modules/modules.js index 22abc8f78e..c0c07613c1 100644 --- a/public/components/settings/modules/modules.js +++ b/public/components/settings/modules/modules.js @@ -16,8 +16,9 @@ import { AppState } from '../../../react-services/app-state'; import WzReduxProvider from '../../../redux/wz-redux-provider'; import store from '../../../redux/store'; import { updateSelectedSettingsSection } from '../../../redux/actions/appStateActions'; -import { withUserAuthorizationPrompt } from '../../common/hocs/withUserAuthorization'; +import { withUserAuthorizationPrompt, withErrorBoundary, withReduxProvider } from '../../common/hocs'; import { WAZUH_ROLE_ADMINISTRATOR_NAME } from '../../../../common/constants'; +import { compose } from 'redux' export class EnableModulesWrapper extends Component { constructor(props) { @@ -165,11 +166,8 @@ export class EnableModulesWrapper extends Component { } } -const WzEnableModulesWithAdministrator = withUserAuthorizationPrompt(null, [WAZUH_ROLE_ADMINISTRATOR_NAME])(EnableModulesWrapper); -export function EnableModules() { - return( - - - - ); -} \ No newline at end of file +export const EnableModules = compose ( + withErrorBoundary, + withReduxProvider, + withUserAuthorizationPrompt(null, [WAZUH_ROLE_ADMINISTRATOR_NAME]) +)(EnableModulesWrapper); \ No newline at end of file diff --git a/public/components/settings/settings-logs/logs.js b/public/components/settings/settings-logs/logs.js index b69b4e3206..bdb642d32c 100644 --- a/public/components/settings/settings-logs/logs.js +++ b/public/components/settings/settings-logs/logs.js @@ -27,8 +27,9 @@ import { import { formatUIDate } from '../../../react-services/time-service'; import store from '../../../redux/store'; import { updateSelectedSettingsSection } from '../../../redux/actions/appStateActions'; +import { withErrorBoundary } from '../../common/hocs'; -export default class SettingsLogs extends Component { +class SettingsLogs extends Component { constructor(props) { super(props); this.offset = 275; @@ -137,3 +138,7 @@ export default class SettingsLogs extends Component { ); } } + +export default withErrorBoundary( + SettingsLogs +) \ No newline at end of file diff --git a/public/controllers/agent/components/agents-preview.js b/public/controllers/agent/components/agents-preview.js index dbc8f9cdbf..4f8ad4f9ad 100644 --- a/public/controllers/agent/components/agents-preview.js +++ b/public/controllers/agent/components/agents-preview.js @@ -21,7 +21,6 @@ import { EuiStat, EuiLoadingChart, EuiSpacer, - EuiText, EuiEmptyPrompt, EuiToolTip } from '@elastic/eui'; @@ -40,8 +39,10 @@ import { WzDatePicker } from '../../../components/wz-date-picker/wz-date-picker' import { withReduxProvider, withGlobalBreadcrumb, withUserAuthorizationPrompt } from '../../../components/common/hocs'; import { formatUIDate } from '../../../../public/react-services/time-service'; import { compose } from 'redux'; +import { withErrorBoundary } from '../../../components/common/hocs' export const AgentsPreview = compose( + withErrorBoundary, withReduxProvider, withGlobalBreadcrumb([{ text: '' }, { text: 'Agents' }]), withUserAuthorizationPrompt([[{action: 'agent:read', resource: 'agent:id:*'},{action: 'agent:read', resource: 'agent:group:*'}]]) diff --git a/public/controllers/agent/components/register-agent.js b/public/controllers/agent/components/register-agent.js index ca150d594b..085689fb37 100644 --- a/public/controllers/agent/components/register-agent.js +++ b/public/controllers/agent/components/register-agent.js @@ -14,14 +14,11 @@ import { version } from '../../../../package.json'; import { WazuhConfig } from '../../../react-services/wazuh-config'; import { EuiSteps, - EuiTabs, EuiTabbedContent, EuiFlexGroup, EuiFlexItem, EuiPanel, - EuiButtonToggle, EuiButtonGroup, - EuiFormRow, EuiComboBox, EuiFieldText, EuiText, @@ -38,7 +35,7 @@ import { EuiCode } from '@elastic/eui'; import { WzRequest } from '../../../react-services/wz-request'; - +import { withErrorBoundary } from '../../../components/common/hocs' const architectureButtons = [ { @@ -114,7 +111,7 @@ const pTextCheckConnectionStyle = { marginTop: '3em', }; -export class RegisterAgent extends Component { +export const RegisterAgent = withErrorBoundary (class RegisterAgent extends Component { constructor(props) { super(props); this.wazuhConfig = new WazuhConfig(); @@ -703,4 +700,4 @@ export class RegisterAgent extends Component {
); } -} +}) From 5ae8a91634f69c9fa67fa406dc24c268b5b97e97 Mon Sep 17 00:00:00 2001 From: pablomarga Date: Mon, 14 Jun 2021 10:41:34 +0200 Subject: [PATCH 18/24] Added hoc --- .../components/agents/stats/agent-stats.tsx | 3 +- .../components/agents/syscollector/main.tsx | 18 +- public/components/common/modules/main.tsx | 11 +- .../common/welcome/agents-welcome.js | 10 +- .../management/cluster/cluster-disabled.js | 6 +- .../management/cluster/cluster-timelions.js | 161 ++++++++---------- .../cluster/cluster-visualization.js | 4 +- public/components/overview/mitre/mitre.tsx | 6 +- public/components/visualize/wz-visualize.js | 5 +- .../wz-agent-selector-wrapper.js | 21 +-- .../components/wz-filter-bar/wz-filter-bar.js | 5 +- public/components/wz-menu/wz-menu-wrapper.js | 20 +-- .../agent/components/agents-table.js | 5 +- .../agent/components/export-configuration.js | 5 +- 14 files changed, 117 insertions(+), 163 deletions(-) diff --git a/public/components/agents/stats/agent-stats.tsx b/public/components/agents/stats/agent-stats.tsx index 0bb652062e..3f0f7059f8 100644 --- a/public/components/agents/stats/agent-stats.tsx +++ b/public/components/agents/stats/agent-stats.tsx @@ -21,7 +21,7 @@ import { EuiText } from '@elastic/eui'; -import { withGlobalBreadcrumb, withReduxProvider, withGuard, withUserAuthorizationPrompt } from '../../common/hocs'; +import { withGlobalBreadcrumb, withReduxProvider, withGuard, withUserAuthorizationPrompt, withErrorBoundary } from '../../common/hocs'; import { compose } from 'redux'; import { WzRequest, formatUIDate } from '../../../react-services'; import { AgentStatTable } from './table'; @@ -80,6 +80,7 @@ const statsAgents: {title: string, field: string, render?: (value) => any}[] = [ ]; export const MainAgentStats = compose( + withErrorBoundary, withReduxProvider, withGlobalBreadcrumb(({agent}) => [ { diff --git a/public/components/agents/syscollector/main.tsx b/public/components/agents/syscollector/main.tsx index 0be7dff02e..a4d71095b8 100644 --- a/public/components/agents/syscollector/main.tsx +++ b/public/components/agents/syscollector/main.tsx @@ -10,16 +10,12 @@ * Find more information about this on the LICENSE file. */ -import React, { Component } from 'react'; -import { SyscollectorInventory } from './inventory' +import React from 'react'; +import { withErrorBoundary } from '../../common/hocs'; +import { SyscollectorInventory } from './inventory'; -export class MainSyscollector extends Component { - - constructor(props) { - super(props); - } - - render() { - return ( ) - } +function MainSyscollectorClass(props) { + return ; } + +export const MainSyscollector = withErrorBoundary (MainSyscollectorClass); diff --git a/public/components/common/modules/main.tsx b/public/components/common/modules/main.tsx index 22d8e5ff34..b3df090ade 100644 --- a/public/components/common/modules/main.tsx +++ b/public/components/common/modules/main.tsx @@ -27,9 +27,10 @@ import { getAngularModule, getDataPlugin, getUiSettings } from '../../../kibana- import { MainModuleAgent } from './main-agent' import { MainModuleOverview } from './main-overview'; import store from '../../../redux/store'; -import WzReduxProvider from '../../../redux/wz-redux-provider.js'; +import { compose } from 'redux'; +import { withReduxProvider,withErrorBoundary } from '../hocs'; -export class MainModule extends Component { +export const MainModule = compose (withErrorBoundary,withReduxProvider) (class MainModule extends Component { constructor(props) { super(props); this.reportingService = new ReportingService(); @@ -211,13 +212,13 @@ export class MainModule extends Component { onSelectedTabChanged: (id) => this.onSelectedTabChanged(id) } return ( - + <> {agent && || ((this.props.section && this.props.section !== 'welcome') && ) } - + ); } -} +}) diff --git a/public/components/common/welcome/agents-welcome.js b/public/components/common/welcome/agents-welcome.js index 01c4637c27..ac768905dc 100644 --- a/public/components/common/welcome/agents-welcome.js +++ b/public/components/common/welcome/agents-welcome.js @@ -23,12 +23,9 @@ import { EuiFlexGrid, EuiButtonEmpty, EuiTitle, - EuiHealth, - EuiHorizontalRule, EuiPage, EuiButton, EuiPopover, - EuiSelect, EuiLoadingChart, EuiToolTip, EuiButtonIcon, @@ -54,8 +51,9 @@ import { updateCurrentAgentData } from '../../../redux/actions/appStateActions'; import WzTextWithTooltipIfTruncated from '../wz-text-with-tooltip-if-truncated'; import { getAngularModule } from '../../../kibana-services'; import { hasAgentSupportModule } from '../../../react-services/wz-agents'; +import { withErrorBoundary } from '../hocs'; -export class AgentsWelcome extends Component { +export const AgentsWelcome = withErrorBoundary (class AgentsWelcome extends Component { _isMount = false; constructor(props) { super(props); @@ -329,7 +327,7 @@ export class AgentsWelcome extends Component { - ); + );withErrorBoundary } @@ -614,4 +612,4 @@ export class AgentsWelcome extends Component {
); } -} +}) diff --git a/public/components/management/cluster/cluster-disabled.js b/public/components/management/cluster/cluster-disabled.js index 979bdc0b94..8222d54a99 100644 --- a/public/components/management/cluster/cluster-disabled.js +++ b/public/components/management/cluster/cluster-disabled.js @@ -1,7 +1,7 @@ import React, { Component, Fragment } from 'react'; import { EuiPage, EuiPageContent, EuiEmptyPrompt } from '@elastic/eui'; - -export class ClusterDisabled extends Component { +import { withErrorBoundary } from '../../common/hocs' +export const ClusterDisabled = withErrorBoundary (class ClusterDisabled extends Component { constructor(props) { super(props); this.state = {}; @@ -43,4 +43,4 @@ export class ClusterDisabled extends Component { ); } -} +}) diff --git a/public/components/management/cluster/cluster-timelions.js b/public/components/management/cluster/cluster-timelions.js index 8d0aaeecbf..63f4f33772 100644 --- a/public/components/management/cluster/cluster-timelions.js +++ b/public/components/management/cluster/cluster-timelions.js @@ -1,107 +1,86 @@ import React, { Component } from 'react'; -import { - EuiFlexGroup, - EuiFlexItem, - EuiPanel, - EuiButtonIcon -} from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiButtonIcon } from '@elastic/eui'; import WzReduxProvider from '../../../redux/wz-redux-provider'; import KibanaVis from '../../../kibana-integrations/kibana-vis'; +import { compose } from 'redux'; +import { withErrorBoundary, withReduxProvider } from '../../common/hocs'; -export class ClusterTimelions extends Component { +export const ClusterTimelions = compose (withErrorBoundary,withReduxProvider) (class ClusterTimelions extends Component { constructor(props) { super(props); this.state = {}; } - expand = id => { + expand = (id) => { this.setState({ expandedVis: this.state.expandedVis === id ? false : id }); }; render() { return ( - - - - - - -

- {'Cluster alerts summary'} -

- - this.expand( - 'Wazuh-App-Cluster-monitoring-Overview-Manager' - ) - } - iconType="expand" - aria-label="Expand" - /> -
-
- - - -
-
-
-
- - - - -

- {'Alerts by node summary'} -

- - this.expand('Wazuh-App-Cluster-monitoring-Overview') - } - iconType="expand" - aria-label="Expand" - /> -
-
- - - -
-
-
-
-
-
+ + + + + +

{'Cluster alerts summary'}

+ this.expand('Wazuh-App-Cluster-monitoring-Overview-Manager')} + iconType="expand" + aria-label="Expand" + /> +
+
+ + + +
+
+
+
+ + + + +

{'Alerts by node summary'}

+ this.expand('Wazuh-App-Cluster-monitoring-Overview')} + iconType="expand" + aria-label="Expand" + /> +
+
+ + + +
+
+
+
+
); } -} +}) diff --git a/public/components/management/cluster/cluster-visualization.js b/public/components/management/cluster/cluster-visualization.js index cf698b57cc..231d77db56 100644 --- a/public/components/management/cluster/cluster-visualization.js +++ b/public/components/management/cluster/cluster-visualization.js @@ -3,10 +3,10 @@ import KibanaVis from '../../../kibana-integrations/kibana-vis'; import { withErrorBoundary, withReduxProvider } from '../../../components/common/hocs'; import {compose} from 'redux' -function KibanaVisClass() { +function KibanaVisClass(props) { return (
- +
); } diff --git a/public/components/overview/mitre/mitre.tsx b/public/components/overview/mitre/mitre.tsx index 71fdbbf884..897e214449 100644 --- a/public/components/overview/mitre/mitre.tsx +++ b/public/components/overview/mitre/mitre.tsx @@ -25,14 +25,14 @@ import { KbnSearchBar } from '../../kbn-search-bar'; import { TimeRange, Query } from '../../../../../../src/plugins/data/common'; import { ModulesHelper } from '../../common/modules/modules-helper'; import { getDataPlugin, getToasts } from '../../../kibana-services'; -import { WzEmptyPromptNoPermissions } from "../../common/permissions/prompt"; +import { withErrorBoundary } from "../../common/hocs" export interface ITactic { [key:string]: string[] } -export class Mitre extends Component { +export const Mitre = withErrorBoundary (class Mitre extends Component { _isMount = false; timefilter: { getTime(): TimeRange @@ -190,5 +190,5 @@ export class Mitre extends Component {
); } -} +}) diff --git a/public/components/visualize/wz-visualize.js b/public/components/visualize/wz-visualize.js index 5cf22181d4..21ffca0f15 100644 --- a/public/components/visualize/wz-visualize.js +++ b/public/components/visualize/wz-visualize.js @@ -36,12 +36,13 @@ import { PatternHandler } from '../../react-services/pattern-handler'; import { getToasts } from '../../kibana-services'; import { SecurityAlerts } from './components'; import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; -import { withReduxProvider } from '../common/hocs'; +import { withReduxProvider,withErrorBoundary } from '../common/hocs'; +import { compose } from 'redux'; const visHandler = new VisHandlers(); -export const WzVisualize = withReduxProvider(class WzVisualize extends Component { +export const WzVisualize = compose (withErrorBoundary,withReduxProvider) (class WzVisualize extends Component { _isMount = false; constructor(props) { super(props); diff --git a/public/components/wz-agent-selector/wz-agent-selector-wrapper.js b/public/components/wz-agent-selector/wz-agent-selector-wrapper.js index f469f8bd8d..fcd52db577 100644 --- a/public/components/wz-agent-selector/wz-agent-selector-wrapper.js +++ b/public/components/wz-agent-selector/wz-agent-selector-wrapper.js @@ -11,22 +11,9 @@ * Find more information about this on the LICENSE file. * */ -import React, { Component } from 'react'; +import React from 'react'; import WzAgentSelector from './wz-agent-selector'; -import WzReduxProvider from '../../redux/wz-redux-provider'; +import { compose } from 'redux'; +import { withErrorBoundary, withReduxProvider } from '../common/hocs'; - -export class WzAgentSelectorWrapper extends Component { - constructor(props) { - super(props); - this.state = {}; - } - - render() { - return ( - - - - ); - } -} +export const WzAgentSelectorWrapper = compose (withErrorBoundary, withReduxProvider)(WzAgentSelector); \ No newline at end of file diff --git a/public/components/wz-filter-bar/wz-filter-bar.js b/public/components/wz-filter-bar/wz-filter-bar.js index 2b0d06332c..ada604ae3c 100644 --- a/public/components/wz-filter-bar/wz-filter-bar.js +++ b/public/components/wz-filter-bar/wz-filter-bar.js @@ -13,8 +13,9 @@ import React, { Component } from 'react'; import { EuiComboBox } from '@elastic/eui'; import PropTypes from 'prop-types'; import './wz-filter-bar.scss'; +import { withErrorBoundary } from '../common/hocs'; -export class WzFilterBar extends Component { +export const WzFilterBar = withErrorBoundary (class WzFilterBar extends Component { constructor(props) { super(props); const { model, selectedOptions } = this.props; @@ -295,7 +296,7 @@ export class WzFilterBar extends Component { /> ); } -} +}); WzFilterBar.propTypes = { clickAction: PropTypes.func, diff --git a/public/components/wz-menu/wz-menu-wrapper.js b/public/components/wz-menu/wz-menu-wrapper.js index 92f5ef409d..5d16e61145 100644 --- a/public/components/wz-menu/wz-menu-wrapper.js +++ b/public/components/wz-menu/wz-menu-wrapper.js @@ -11,22 +11,10 @@ * Find more information about this on the LICENSE file. * */ -import React, { Component } from 'react'; +import React from 'react'; import WzMenu from './wz-menu'; -import WzReduxProvider from '../../redux/wz-redux-provider'; import './wz-menu.scss'; +import { compose } from 'redux'; +import { withErrorBoundary, withReduxProvider } from '../common/hocs'; -export class WzMenuWrapper extends Component { - constructor(props) { - super(props); - this.state = {}; - } - - render() { - return ( - - - - ); - } -} +export const WzMenuWrapper = compose (withErrorBoundary, withReduxProvider)(WzMenu); diff --git a/public/controllers/agent/components/agents-table.js b/public/controllers/agent/components/agents-table.js index 077f4f78e5..1e1432b246 100644 --- a/public/controllers/agent/components/agents-table.js +++ b/public/controllers/agent/components/agents-table.js @@ -40,8 +40,9 @@ import { WzSearchBar, filtersToObject } from '../../../components/wz-search-bar' import { getAgentFilterValues } from '../../../controllers/management/components/management/groups/get-agents-filters-values'; import { WzButtonPermissions } from '../../../components/common/permissions/button'; import { formatUIDate } from '../../../react-services/time-service'; +import { withErrorBoundary } from '../../../components/common/hocs' -export class AgentsTable extends Component { +export const AgentsTable = withErrorBoundary (class AgentsTable extends Component { _isMount = false; constructor(props) { super(props); @@ -1024,7 +1025,7 @@ export class AgentsTable extends Component {
); } -} +}); AgentsTable.propTypes = { wzReq: PropTypes.func, diff --git a/public/controllers/agent/components/export-configuration.js b/public/controllers/agent/components/export-configuration.js index 004b428b6a..40dda2331b 100644 --- a/public/controllers/agent/components/export-configuration.js +++ b/public/controllers/agent/components/export-configuration.js @@ -22,8 +22,9 @@ import { import PropTypes from 'prop-types'; import { UnsupportedComponents } from '../../../utils/components-os-support'; import { WAZUH_AGENTS_OS_TYPE } from '../../../../common/constants'; +import { withErrorBoundary } from '../../../components/common/hocs'; -export class ExportConfiguration extends Component { +export const ExportConfiguration = withErrorBoundary (class ExportConfiguration extends Component { constructor(props) { super(props); @@ -163,7 +164,7 @@ export class ExportConfiguration extends Component { ); } -} +}); ExportConfiguration.propTypes = { exportConfiguration: PropTypes.func, From b0d519c5aec579b04342541e968a17ff92fc0cc5 Mon Sep 17 00:00:00 2001 From: pablomarga Date: Mon, 14 Jun 2021 15:33:53 +0200 Subject: [PATCH 19/24] Before rebase --- .../add-modules-data/WzSampleDataWrapper.js | 4 +- .../common/modules/discover/discover.tsx | 3 +- .../welcome/management-welcome-wrapper.js | 19 ++----- .../wz-agent-selector-wrapper.js | 1 - .../configuration/configuration-main.js | 52 +++++++------------ .../management/management-provider.js | 24 +++------ 6 files changed, 31 insertions(+), 72 deletions(-) diff --git a/public/components/add-modules-data/WzSampleDataWrapper.js b/public/components/add-modules-data/WzSampleDataWrapper.js index d3c6331863..7f00da1c50 100644 --- a/public/components/add-modules-data/WzSampleDataWrapper.js +++ b/public/components/add-modules-data/WzSampleDataWrapper.js @@ -62,9 +62,7 @@ export class WzSampleDataProvider extends Component { - - - + diff --git a/public/components/common/modules/discover/discover.tsx b/public/components/common/modules/discover/discover.tsx index 0d99e5d902..868ccfb3d3 100644 --- a/public/components/common/modules/discover/discover.tsx +++ b/public/components/common/modules/discover/discover.tsx @@ -21,7 +21,7 @@ import { WazuhConfig } from '../../../../react-services/wazuh-config'; import { formatUIDate } from '../../../../react-services/time-service'; import { KbnSearchBar } from '../../../kbn-search-bar'; import { FlyoutTechnique } from '../../../../components/overview/mitre/components/techniques/components/flyout-technique'; -import { withReduxProvider } from '../../../common/hocs'; +import { withErrorBoundary, withReduxProvider } from '../../../common/hocs'; import { connect } from 'react-redux'; import { compose } from 'redux'; import _ from 'lodash'; @@ -57,6 +57,7 @@ const mapStateToProps = state => ({ }); export const Discover = compose( + withErrorBoundary, withReduxProvider, connect(mapStateToProps) )(class Discover extends Component { diff --git a/public/components/common/welcome/management-welcome-wrapper.js b/public/components/common/welcome/management-welcome-wrapper.js index bb2073a578..0652ada40b 100644 --- a/public/components/common/welcome/management-welcome-wrapper.js +++ b/public/components/common/welcome/management-welcome-wrapper.js @@ -12,22 +12,9 @@ * * DELETE THIS WRAPPER WHEN WELCOME SCREEN WAS NOT BE CALLED FROM ANGULARJS */ -import React, { Component } from 'react'; import ManagementWelcome from './management-welcome'; -import WzReduxProvider from '../../../redux/wz-redux-provider'; import './welcome.scss'; +import { compose } from 'redux'; +import { withErrorBoundary, withReduxProvider } from '../hocs'; -export class ManagementWelcomeWrapper extends Component { - constructor(props) { - super(props); - this.state = {}; - } - - render() { - return ( - - - - ); - } -} +export const ManagementWelcomeWrapper = compose (withErrorBoundary,withReduxProvider)(ManagementWelcome) diff --git a/public/components/wz-agent-selector/wz-agent-selector-wrapper.js b/public/components/wz-agent-selector/wz-agent-selector-wrapper.js index fcd52db577..9bef47cc22 100644 --- a/public/components/wz-agent-selector/wz-agent-selector-wrapper.js +++ b/public/components/wz-agent-selector/wz-agent-selector-wrapper.js @@ -11,7 +11,6 @@ * Find more information about this on the LICENSE file. * */ -import React from 'react'; import WzAgentSelector from './wz-agent-selector'; import { compose } from 'redux'; import { withErrorBoundary, withReduxProvider } from '../common/hocs'; diff --git a/public/controllers/management/components/management/configuration/configuration-main.js b/public/controllers/management/components/management/configuration/configuration-main.js index 6a4980a58c..b5993a92a8 100644 --- a/public/controllers/management/components/management/configuration/configuration-main.js +++ b/public/controllers/management/components/management/configuration/configuration-main.js @@ -10,51 +10,37 @@ * Find more information about this on the LICENSE file. */ -import React, { Component } from 'react'; -import WzReduxProvider from '../../../../../redux/wz-redux-provider'; import WzConfigurationSwitch from './configuration-switch'; -import { updateGlobalBreadcrumb } from '../../../../../redux/actions/globalBreadcrumbActions'; -import store from '../../../../../redux/store'; +import { + withErrorBoundary, + withGlobalBreadcrumb, + withReduxProvider, +} from '../../../../../components/common/hocs'; +import { compose } from 'redux' -class WzConfigurationMain extends Component { - constructor(props) { - super(props); - } - - setGlobalBreadcrumb() { +export default compose( + withErrorBoundary, + withReduxProvider, + withGlobalBreadcrumb((props) => { let breadcrumb = false; - if (this.props.agent.id === '000') { + if (props.agent.id === '000') { breadcrumb = [ { text: '' }, { text: 'Management', href: '/app/wazuh#/manager' }, - { text: 'Configuration' } + { text: 'Configuration' }, ]; } else { breadcrumb = [ { text: '' }, { text: 'Agents', - href: '#/agents-preview' + href: '#/agents-preview', }, - { agent: this.props.agent }, - { text: 'Configuration' } + { agent: props.agent }, + { text: 'Configuration' }, ]; } - store.dispatch(updateGlobalBreadcrumb(breadcrumb)); - $('#breadcrumbNoTitle').attr("title",""); - } - - async componentDidMount() { - this.setGlobalBreadcrumb(); - } - - render() { - return ( - - - - ); - } -} - -export default WzConfigurationMain; + $('#breadcrumbNoTitle').attr('title', ''); + return breadcrumb; + }) +)(WzConfigurationSwitch); diff --git a/public/controllers/management/components/management/management-provider.js b/public/controllers/management/components/management/management-provider.js index e370f80015..6d2dbcf586 100644 --- a/public/controllers/management/components/management/management-provider.js +++ b/public/controllers/management/components/management/management-provider.js @@ -1,21 +1,9 @@ -import React, { Component } from 'react'; +import { compose } from 'redux'; +import { withErrorBoundary, withReduxProvider } from '../../../../components/common/hocs'; // Redux -import store from '../../../../redux/store'; -import WzReduxProvider from '../../../../redux/wz-redux-provider'; import WzManagementMain from '../management/management-main'; -export default class WzManagement extends Component { - constructor(props) { - super(props); - this.state = {}; - this.store = store; - } - - render() { - return ( - - - - ); - } -} +export default compose( + withErrorBoundary, + withReduxProvider +)(WzManagementMain); \ No newline at end of file From e4e73eeae79ed6b4f6c224d99b657c2d631848a6 Mon Sep 17 00:00:00 2001 From: pablomarga Date: Mon, 14 Jun 2021 16:30:30 +0200 Subject: [PATCH 20/24] rebase 4.3-7.10 --- .../error-boundary.test.tsx.snap | 82 ++++++------- .../error-boundary/error-boundary.test.tsx | 24 +--- .../common/error-boundary/error-boundary.tsx | 111 ++++++++---------- .../with-error-boundary.test.tsx.snap | 84 ++++++------- .../with-error-boundary.test.tsx | 11 +- 5 files changed, 133 insertions(+), 179 deletions(-) diff --git a/public/components/common/error-boundary/__snapshots__/error-boundary.test.tsx.snap b/public/components/common/error-boundary/__snapshots__/error-boundary.test.tsx.snap index 0c0bd88dd0..36bcd994ea 100644 --- a/public/components/common/error-boundary/__snapshots__/error-boundary.test.tsx.snap +++ b/public/components/common/error-boundary/__snapshots__/error-boundary.test.tsx.snap @@ -1,8 +1,8 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`ErrorBoundary component renders correctly to match the snapshot 1`] = ` +exports[`ErrorBoundary component renders correctly and check snapshot 1`] = ` - -
- - Error: I crashed! I crash very hard - -
- - + "componentStack": " in ComponentWithError in ErrorBoundary (created by WrapperComponent) - in WrapperComponent - -
- + in WrapperComponent", + } + } + errorTitle={[Error: I crashed!]} + style={null} + /> } iconType="faceSad" title={ @@ -114,40 +97,49 @@ exports[`ErrorBoundary component renders correctly to match the snapshot 1`] = `
-
-
- - Error: I crashed! I crash very hard - -
- +
+ Error: I crashed! +
in ComponentWithError in ErrorBoundary (created by WrapperComponent) in WrapperComponent - -
-
+ +
+
-
+
`; diff --git a/public/components/common/error-boundary/error-boundary.test.tsx b/public/components/common/error-boundary/error-boundary.test.tsx index cd5ca53a33..a96db0b6e5 100644 --- a/public/components/common/error-boundary/error-boundary.test.tsx +++ b/public/components/common/error-boundary/error-boundary.test.tsx @@ -1,17 +1,3 @@ -/* - * Wazuh app - React test for ErrorBoundary component. - * - * Copyright (C) 2015-2021 Wazuh, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Find more information about this on the LICENSE file. - * - */ - import React from 'react'; import { mount } from 'enzyme'; import ErrorBoundary from './error-boundary'; @@ -20,11 +6,11 @@ jest.mock('loglevel'); describe('ErrorBoundary component', () => { const ComponentWithError = () => { - throw new Error('I crashed! I crash very hard'); + throw new Error('I crashed!'); return <>; }; - it('renders correctly to match the snapshot', () => { + it('renders correctly and check snapshot', () => { const wrapper = mount( @@ -40,12 +26,6 @@ describe('ErrorBoundary component', () => { ); - - expect(wrapper.find('EuiTitle').exists()).toBeTruthy(); - expect(wrapper.find('EuiText').exists('details')).toBeTruthy(); expect(wrapper.find('EuiTitle').find('h2').text().trim()).toBe('Something went wrong.'); - expect(wrapper.find('EuiText').find('details').find('span').at(0).text()).toBe( - 'Error: I crashed! I crash very hard' - ); }); }); diff --git a/public/components/common/error-boundary/error-boundary.tsx b/public/components/common/error-boundary/error-boundary.tsx index 395f9f3152..b88c79b85c 100644 --- a/public/components/common/error-boundary/error-boundary.tsx +++ b/public/components/common/error-boundary/error-boundary.tsx @@ -1,36 +1,25 @@ /* - * Wazuh app - React component for catch and handles rendering errors. - * - * Copyright (C) 2015-2021 Wazuh, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Find more information about this on the LICENSE file. - * - */ +* Wazuh app - React component for catch and handles rendering errors. +* +* Copyright (C) 2015-2021 Wazuh, Inc. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* Find more information about this on the LICENSE file. +* +*/ import React, { Component } from 'react'; -import loglevel from 'loglevel'; -import { UI_LOGGER_LEVELS } from '../../../../common/constants'; -import { - UI_ERROR_SEVERITIES, - UIErrorLog, - UIErrorSeverity, - UILogLevel, -} from '../../../react-services/error-orchestrator/types'; -import { ErrorOrchestratorService } from '../../../react-services'; -import { ErrorComponentPrompt } from '../error-boundary-prompt/error-boundary-prompt'; +import log from 'loglevel'; +import { EuiEmptyPrompt } from '@elastic/eui'; export default class ErrorBoundary extends Component { - private logger: ErrorOrchestratorService; constructor(props) { super(props); this.state = { errorTitle: null, errorInfo: null, style: null }; - this.context = this.constructor.displayName || this.constructor.name || undefined; - this.logger = new ErrorOrchestratorService(); } componentDidCatch = (errorTitle, errorInfo) => catchFunc(errorTitle, errorInfo, this); @@ -39,47 +28,51 @@ export default class ErrorBoundary extends Component { const { errorTitle, style, errorInfo }: Readonly = this.state; if (errorInfo) { - return ; + return ; } return this.props.children; } } const catchFunc = (errorTitle, errorInfo, ctx) => { - try { - ctx.setState({ - errorTitle: errorTitle, - errorInfo: errorInfo, - }); + ctx.setState({ + errorTitle: errorTitle, + errorInfo: errorInfo, + }); - const options: UIErrorLog = { - context: ctx.context, - level: UI_LOGGER_LEVELS.WARNING as UILogLevel, - severity: UI_ERROR_SEVERITIES.UI as UIErrorSeverity, - display: false, - store: true, - error: { - error: errorTitle.name, - message: errorTitle.message, - title: errorTitle.toString(), - }, - }; + log.error({ errorTitle, errorInfo }); +}; - ctx.logger.handleError(options); - } catch (error) { - const optionsCatch: UIErrorLog = { - context: ctx.context, - level: UI_LOGGER_LEVELS.ERROR as UILogLevel, - severity: UI_ERROR_SEVERITIES.UI as UIErrorSeverity, - display: false, - store: true, - error: { - error: error, - message: error?.message || '', - title: '', - }, - }; +const ErrorComponent = (props: { errorTitle: any; errorInfo: any; style: any }) => { + const styles = { + error: { + borderTop: '1px solid #777', + borderBottom: '1px solid #777', + padding: '12px', + }, + }; - ctx.logger.handleError(optionsCatch); - } + return ( +
+
+ {props.errorTitle && props.errorTitle.toString()} +
+ {props.errorInfo.componentStack} +
+
+ ); }; + +const HandleError = (props: { errorTitle: any; errorInfo: any; style: any }) => ( + Something went wrong.} + body={ + + } + /> +); diff --git a/public/components/common/hocs/error-boundary/__snapshots__/with-error-boundary.test.tsx.snap b/public/components/common/hocs/error-boundary/__snapshots__/with-error-boundary.test.tsx.snap index 24212ec32e..a107118897 100644 --- a/public/components/common/hocs/error-boundary/__snapshots__/with-error-boundary.test.tsx.snap +++ b/public/components/common/hocs/error-boundary/__snapshots__/with-error-boundary.test.tsx.snap @@ -1,9 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`withErrorBoundary hoc implementation renders correctly to match the snapshot 1`] = ` +exports[`withErrorBoundary hoc implementation renders correctly and check snapshot 1`] = ` - -
- - Error: I crashed! I crash very hard - -
- - + "componentStack": " in ComponentWithError in Unknown in ErrorBoundary in Unknown (created by WrapperComponent) - in WrapperComponent - -
- + in WrapperComponent", + } + } + errorTitle={[Error: I crashed!]} + style={null} + /> } iconType="faceSad" title={ @@ -119,43 +102,54 @@ exports[`withErrorBoundary hoc implementation renders correctly to match the sna
-
-
- - Error: I crashed! I crash very hard - -
- +
+ Error: I crashed! +
in ComponentWithError in Unknown in ErrorBoundary in Unknown (created by WrapperComponent) in WrapperComponent - -
-
+ +
+
-
+ `; diff --git a/public/components/common/hocs/error-boundary/with-error-boundary.test.tsx b/public/components/common/hocs/error-boundary/with-error-boundary.test.tsx index 6b6e7c37c6..abd64b7c07 100644 --- a/public/components/common/hocs/error-boundary/with-error-boundary.test.tsx +++ b/public/components/common/hocs/error-boundary/with-error-boundary.test.tsx @@ -6,11 +6,11 @@ jest.mock('loglevel'); describe('withErrorBoundary hoc implementation', () => { const ComponentWithError = () => { - throw new Error('I crashed! I crash very hard'); + throw new Error('I crashed!'); return <>; - }; + } - it('renders correctly to match the snapshot', () => { + it('renders correctly and check snapshot', () => { const ErrorComponentWithHoc = withErrorBoundary(() => ); const wrapper = mount(); @@ -21,11 +21,6 @@ describe('withErrorBoundary hoc implementation', () => { const ErrorComponentWithHoc = withErrorBoundary(() => ); const wrapper = mount(); - expect(wrapper.find('EuiTitle').exists()).toBeTruthy(); - expect(wrapper.find('EuiText').exists('details')).toBeTruthy(); expect(wrapper.find('EuiTitle').find('h2').text().trim()).toBe('Something went wrong.'); - expect(wrapper.find('EuiText').find('details').find('span').at(0).text()).toBe( - 'Error: I crashed! I crash very hard' - ); }); }); From d9697bec29933ea96c2f214d9445f2f10612f2fe Mon Sep 17 00:00:00 2001 From: Maximiliano Ibarra Date: Mon, 7 Jun 2021 16:23:52 -0300 Subject: [PATCH 21/24] Feature/3316 error handler orchestrator (#3327) * feat(errorBoundary): Added ErrorBoundary HOC and component and added loglevel dependency * feature(errorBoundary): Moved with the others HOCs. * feature(errorBoundary): Typo refactor. * First attempt LoggerService * Merged error boundary, integrated loggerService. * changed logger name, create logger-service test file * Updated CHANGELOG * Moved to react-services, changed name, traslates comments * feat(errorBoundary): Removed old integration * refactor(loggerService): Changed class for function methods. * test(logger-service): Added basic unit test to logger-service * refactor(logger-service): Applied new implementation of error-orchestrator service. * feature(logger-service): PR comments and some refactors. Co-authored-by: gabiwassan Co-authored-by: Ibarra Maximiliano --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab711cccf7..4c8bac1daf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,6 @@ All notable changes to the Wazuh app project will be documented in this file. - Changed ossec to wazuh in sample-data [#3121](https://github.com/wazuh/wazuh-kibana-app/pull/3121) - Changed empty fields in FIM tables and `syscheck.value_name` in discovery now show an empty tag for visual clarity [#3279](https://github.com/wazuh/wazuh-kibana-app/pull/3279) - ## Wazuh v4.2.0 - Kibana 7.10.2 , 7.11.2 - Revision 4201 ### Added From 1706f5664393cf2672ff32d302d7b815af59550d Mon Sep 17 00:00:00 2001 From: pablomarga Date: Mon, 14 Jun 2021 17:52:31 +0200 Subject: [PATCH 22/24] Final implementations --- .../agents-current-section-wrapper.tsx | 20 ++++--------------- .../overview-current-section-wrapper.tsx | 20 ++++--------------- .../components/common/permissions/prompt.tsx | 13 +++++------- .../common/welcome/overview-welcome.js | 5 +++-- .../components/health-check/health-check.tsx | 5 +++-- .../management/cluster/node-list.tsx | 5 +++-- .../groups/multiple-agent-selector.tsx | 5 +++-- public/components/overview/mitre/mitre.tsx | 1 - public/components/security/main.tsx | 3 ++- .../overview/components/requirement-card.js | 5 +++-- .../overview/components/select-agent.js | 5 +++-- .../controllers/overview/components/stats.js | 5 +++-- .../wz-logtest/components/logtest.tsx | 3 ++- 13 files changed, 38 insertions(+), 57 deletions(-) diff --git a/public/components/common/modules/agents-current-section-wrapper.tsx b/public/components/common/modules/agents-current-section-wrapper.tsx index cb5f5921c1..1c9b46700d 100644 --- a/public/components/common/modules/agents-current-section-wrapper.tsx +++ b/public/components/common/modules/agents-current-section-wrapper.tsx @@ -10,20 +10,8 @@ * * Find more information about this on the LICENSE file. */ -import React, { Component } from 'react'; -import WzReduxProvider from '../../../redux/wz-redux-provider'; -import WzCurrentAgentsSection from './agents-current-section' +import WzCurrentAgentsSection from './agents-current-section'; +import { compose } from 'redux'; +import { withErrorBoundary, withReduxProvider } from '../hocs'; -export class WzCurrentAgentsSectionWrapper extends Component { - constructor(props) { - super(props); - } - - render() { - return ( - - - - ); - } -} \ No newline at end of file +export const WzCurrentAgentsSectionWrapper = compose (withErrorBoundary, withReduxProvider) (WzCurrentAgentsSection); diff --git a/public/components/common/modules/overview-current-section-wrapper.tsx b/public/components/common/modules/overview-current-section-wrapper.tsx index e3f09aa96a..103493d686 100644 --- a/public/components/common/modules/overview-current-section-wrapper.tsx +++ b/public/components/common/modules/overview-current-section-wrapper.tsx @@ -10,20 +10,8 @@ * * Find more information about this on the LICENSE file. */ -import React, { Component } from 'react'; -import WzReduxProvider from '../../../redux/wz-redux-provider'; -import WzCurrentOverviewSection from './overview-current-section' +import { compose } from 'redux'; +import { withErrorBoundary, withReduxProvider } from '../hocs'; +import WzCurrentOverviewSection from './overview-current-section'; -export class WzCurrentOverviewSectionWrapper extends Component { - constructor(props) { - super(props); - } - - render() { - return ( - - - - ); - } -} \ No newline at end of file +export const WzCurrentOverviewSectionWrapper = compose (withErrorBoundary, withReduxProvider) (WzCurrentOverviewSection); diff --git a/public/components/common/permissions/prompt.tsx b/public/components/common/permissions/prompt.tsx index 4203c90e4c..4ef609b54f 100644 --- a/public/components/common/permissions/prompt.tsx +++ b/public/components/common/permissions/prompt.tsx @@ -16,6 +16,7 @@ import { useUserRolesRequirements } from '../hooks/useUserRoles'; import { EuiEmptyPrompt, EuiSpacer, EuiPanel } from '@elastic/eui'; import { TUserPermissions, TUserPermissionsFunction, TUserRoles, TUserRolesFunction } from '../permissions/button'; import { WzPermissionsFormatted } from './format'; +import { withErrorBoundary } from '../hocs'; interface IEmptyPromptNoPermissions{ permissions?: TUserPermissions @@ -23,8 +24,8 @@ interface IEmptyPromptNoPermissions{ actions?: React.ReactNode } -export const WzEmptyPromptNoPermissions = ({permissions, roles, actions}: IEmptyPromptNoPermissions) => { - const prompt = ( { + return You have no permissions} body={ @@ -44,12 +45,8 @@ export const WzEmptyPromptNoPermissions = ({permissions, roles, actions}: IEmpty } actions={actions} - />) - return ( - // {prompt} - prompt - ) -} + /> +}); interface IPromptNoPermissions{ permissions?: TUserPermissions | TUserPermissionsFunction diff --git a/public/components/common/welcome/overview-welcome.js b/public/components/common/welcome/overview-welcome.js index 520f888ebe..0f94fd6c4c 100644 --- a/public/components/common/welcome/overview-welcome.js +++ b/public/components/common/welcome/overview-welcome.js @@ -31,8 +31,9 @@ import { updateCurrentTab } from '../../../redux/actions/appStateActions'; import store from '../../../redux/store'; import './welcome.scss'; import { WAZUH_MODULES } from '../../../../common/wazuh-modules'; +import { withErrorBoundary } from '../hocs'; -export class OverviewWelcome extends Component { +export const OverviewWelcome = withErrorBoundary (class OverviewWelcome extends Component { constructor(props) { super(props); this.strtools = new StringsTools(); @@ -186,4 +187,4 @@ export class OverviewWelcome extends Component { ); } -} \ No newline at end of file +}) \ No newline at end of file diff --git a/public/components/health-check/health-check.tsx b/public/components/health-check/health-check.tsx index 53aa528e16..5eae460b61 100644 --- a/public/components/health-check/health-check.tsx +++ b/public/components/health-check/health-check.tsx @@ -13,8 +13,9 @@ import { WAZUH_ERROR_DAEMONS_NOT_READY, WAZUH_INDEX_TYPE_STATISTICS, WAZUH_INDEX import { checkKibanaSettings, checkKibanaSettingsTimeFilter, checkKibanaSettingsMaxBuckets } from './lib'; import store from '../../redux/store'; import { updateWazuhNotReadyYet } from '../../redux/actions/appStateActions.js'; +import { withErrorBoundary } from '../common/hocs'; -export class HealthCheck extends Component { +export const HealthCheck = withErrorBoundary (class HealthCheck extends Component { checkPatternCount = 0; constructor(props) { super(props); @@ -502,4 +503,4 @@ export class HealthCheck extends Component { ); } -}; +}); diff --git a/public/components/management/cluster/node-list.tsx b/public/components/management/cluster/node-list.tsx index e57129e691..d383bdf948 100644 --- a/public/components/management/cluster/node-list.tsx +++ b/public/components/management/cluster/node-list.tsx @@ -1,8 +1,9 @@ import React, { Component } from 'react'; import { EuiPanel, EuiFlexGroup, EuiFlexItem, EuiToolTip, EuiButtonIcon, EuiTitle, EuiInMemoryTable, EuiFieldSearch } from '@elastic/eui'; import { WzRequest } from '../../../react-services/wz-request'; +import { withErrorBoundary } from '../../common/hocs'; -export class NodeList extends Component { +export const NodeList = withErrorBoundary (class NodeList extends Component { constructor(props) { super(props); this.state = { @@ -109,4 +110,4 @@ export class NodeList extends Component { ); } -}; +}); diff --git a/public/components/management/groups/multiple-agent-selector.tsx b/public/components/management/groups/multiple-agent-selector.tsx index 0c73835a04..aab5fde185 100644 --- a/public/components/management/groups/multiple-agent-selector.tsx +++ b/public/components/management/groups/multiple-agent-selector.tsx @@ -19,8 +19,9 @@ import { WzRequest } from '../../../react-services/wz-request'; import './multiple-agent-selector.scss' import $ from 'jquery'; import { WzFieldSearchDelay } from '../../common/search'; +import { withErrorBoundary } from '../../common/hocs'; -export class MultipleAgentSelector extends Component { +export const MultipleAgentSelector = withErrorBoundary (class MultipleAgentSelector extends Component { constructor(props) { super(props); this.state = { @@ -672,4 +673,4 @@ export class MultipleAgentSelector extends Component { ); } -}; +}); diff --git a/public/components/overview/mitre/mitre.tsx b/public/components/overview/mitre/mitre.tsx index 897e214449..257955f35f 100644 --- a/public/components/overview/mitre/mitre.tsx +++ b/public/components/overview/mitre/mitre.tsx @@ -31,7 +31,6 @@ export interface ITactic { [key:string]: string[] } - export const Mitre = withErrorBoundary (class Mitre extends Component { _isMount = false; timefilter: { diff --git a/public/components/security/main.tsx b/public/components/security/main.tsx index 511ac59578..22f6844bb3 100644 --- a/public/components/security/main.tsx +++ b/public/components/security/main.tsx @@ -19,7 +19,7 @@ import { API_USER_STATUS_RUN_AS } from '../../../server/lib/cache-api-user-has-r import { AppState } from '../../react-services/app-state'; import { ErrorHandler } from '../../react-services/error-handler'; import { RolesMapping } from './roles-mapping/roles-mapping'; -import { withReduxProvider, withGlobalBreadcrumb, withUserAuthorizationPrompt } from '../common/hocs'; +import { withReduxProvider, withGlobalBreadcrumb, withUserAuthorizationPrompt, withErrorBoundary } from '../common/hocs'; import { compose } from 'redux'; import { WAZUH_ROLE_ADMINISTRATOR_NAME } from '../../../common/constants'; import { updateSecuritySection } from '../../redux/actions/securityActions'; @@ -48,6 +48,7 @@ const tabs = [ ]; export const WzSecurity = compose( + withErrorBoundary, withReduxProvider, withGlobalBreadcrumb([{ text: '' }, { text: 'Security' }]), withUserAuthorizationPrompt(null, [WAZUH_ROLE_ADMINISTRATOR_NAME]) diff --git a/public/controllers/overview/components/requirement-card.js b/public/controllers/overview/components/requirement-card.js index 0c2818872a..883e405c45 100644 --- a/public/controllers/overview/components/requirement-card.js +++ b/public/controllers/overview/components/requirement-card.js @@ -19,8 +19,9 @@ import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; +import { withErrorBoundary } from '../../../components/common/hocs'; -export class RequirementCard extends Component { +export const RequirementCard = withErrorBoundary (class RequirementCard extends Component { constructor(props) { super(props); this.state = { @@ -171,7 +172,7 @@ export class RequirementCard extends Component { ); } -} +}); RequirementCard.propTypes = { items: PropTypes.array, diff --git a/public/controllers/overview/components/select-agent.js b/public/controllers/overview/components/select-agent.js index c5f5b54f6c..3bda19e17d 100644 --- a/public/controllers/overview/components/select-agent.js +++ b/public/controllers/overview/components/select-agent.js @@ -26,8 +26,9 @@ import { // import { WzRequest } from '../../../../react-services/wz-request'; import { WzRequest } from '../../../react-services/wz-request'; +import { withErrorBoundary } from '../../../components/common/hocs'; -export class SelectAgent extends Component { +export const SelectAgent = withErrorBoundary (class SelectAgent extends Component { constructor(props) { super(props); @@ -274,7 +275,7 @@ export class SelectAgent extends Component { ); } -} +}); SelectAgent.propTypes = { items: PropTypes.array diff --git a/public/controllers/overview/components/stats.js b/public/controllers/overview/components/stats.js index e866c01331..58ce8323d5 100644 --- a/public/controllers/overview/components/stats.js +++ b/public/controllers/overview/components/stats.js @@ -13,8 +13,9 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { EuiStat, EuiFlexItem, EuiFlexGroup, EuiPage, EuiToolTip } from '@elastic/eui'; +import { withErrorBoundary } from '../../../components/common/hocs'; -export class Stats extends Component { +export const Stats = withErrorBoundary (class Stats extends Component { constructor(props) { super(props); @@ -123,7 +124,7 @@ export class Stats extends Component { ); } -} +}); Stats.propTypes = { total: PropTypes.any, diff --git a/public/directives/wz-logtest/components/logtest.tsx b/public/directives/wz-logtest/components/logtest.tsx index c1a7df3676..adf4f91a3a 100644 --- a/public/directives/wz-logtest/components/logtest.tsx +++ b/public/directives/wz-logtest/components/logtest.tsx @@ -27,7 +27,7 @@ import { EuiTitle, } from '@elastic/eui'; import { WzRequest } from '../../../react-services'; -import { withReduxProvider, withUserAuthorizationPrompt } from '../../../components/common/hocs'; +import { withErrorBoundary, withReduxProvider, withUserAuthorizationPrompt } from '../../../components/common/hocs'; import { compose } from 'redux'; type LogstestProps = { @@ -38,6 +38,7 @@ type LogstestProps = { }; export const Logtest = compose( + withErrorBoundary, withReduxProvider, withUserAuthorizationPrompt([{ action: 'logtest:run', resource: `*:*:*` }]) )((props: LogstestProps) => { From 0edc654cba4cd6e461d66d988ce9c8217848c1e6 Mon Sep 17 00:00:00 2001 From: pablomarga Date: Tue, 15 Jun 2021 13:24:05 +0200 Subject: [PATCH 23/24] Fix errors and apply prettier --- .../components/agents/syscollector/main.tsx | 6 +- public/components/common/modules/main.tsx | 336 +++++++++--------- .../components/common/permissions/prompt.tsx | 114 +++--- .../common/welcome/agents-welcome.js | 3 +- .../cluster/cluster-visualization.js | 11 +- .../wz-agent-selector-wrapper.js | 5 +- 6 files changed, 259 insertions(+), 216 deletions(-) diff --git a/public/components/agents/syscollector/main.tsx b/public/components/agents/syscollector/main.tsx index a4d71095b8..958802f450 100644 --- a/public/components/agents/syscollector/main.tsx +++ b/public/components/agents/syscollector/main.tsx @@ -14,8 +14,4 @@ import React from 'react'; import { withErrorBoundary } from '../../common/hocs'; import { SyscollectorInventory } from './inventory'; -function MainSyscollectorClass(props) { - return ; -} - -export const MainSyscollector = withErrorBoundary (MainSyscollectorClass); +export const MainSyscollector = withErrorBoundary(SyscollectorInventory); diff --git a/public/components/common/modules/main.tsx b/public/components/common/modules/main.tsx index b3df090ade..02e7fa9e0d 100644 --- a/public/components/common/modules/main.tsx +++ b/public/components/common/modules/main.tsx @@ -30,195 +30,213 @@ import store from '../../../redux/store'; import { compose } from 'redux'; import { withReduxProvider,withErrorBoundary } from '../hocs'; -export const MainModule = compose (withErrorBoundary,withReduxProvider) (class MainModule extends Component { - constructor(props) { - super(props); - this.reportingService = new ReportingService(); - this.state = { - selectView: false, - loadingReport: false, - switchModule: false, - showAgentInfo: false - }; - const app = getAngularModule(); - this.$rootScope = app.$injector.get('$rootScope'); - } - - async componentDidMount() { - if (!(ModulesDefaults[this.props.section] || {}).notModule) { - this.tabs = (ModulesDefaults[this.props.section] || {}).tabs || [{ id: 'dashboard', name: 'Dashboard' }, { id: 'events', name: 'Events' }]; - this.buttons = (ModulesDefaults[this.props.section] || {}).buttons || ['reporting', 'settings']; +export const MainModule = compose( + withErrorBoundary, + withReduxProvider +)( + class MainModule extends Component { + constructor(props) { + super(props); + this.reportingService = new ReportingService(); + this.state = { + selectView: false, + loadingReport: false, + switchModule: false, + showAgentInfo: false, + }; + const app = getAngularModule(); + this.$rootScope = app.$injector.get('$rootScope'); } - } - componentWillUnmount() { - const { filterManager } = getDataPlugin().query; - if (filterManager.getFilters() && filterManager.getFilters().length) { - filterManager.removeAll(); + async componentDidMount() { + if (!(ModulesDefaults[this.props.section] || {}).notModule) { + this.tabs = (ModulesDefaults[this.props.section] || {}).tabs || [ + { id: 'dashboard', name: 'Dashboard' }, + { id: 'events', name: 'Events' }, + ]; + this.buttons = (ModulesDefaults[this.props.section] || {}).buttons || [ + 'reporting', + 'settings', + ]; + } } - } - canBeInit(tab) { //checks if the init table can be set - let canInit = false; - this.tabs.forEach(element => { - if (element.id === tab && (!element.onlyAgent || (element.onlyAgent && this.props.agent))) { - canInit = true; + componentWillUnmount() { + const { filterManager } = getDataPlugin().query; + if (filterManager.getFilters() && filterManager.getFilters().length) { + filterManager.removeAll(); } - }); - return canInit; - } - - renderTabs(agent = false) { - const { selectView } = this.state; - if (!agent) { + } + canBeInit(tab) { + //checks if the init table can be set + let canInit = false; + this.tabs.forEach((element) => { + if (element.id === tab && (!element.onlyAgent || (element.onlyAgent && this.props.agent))) { + canInit = true; + } + }); + return canInit; } - return ( - - - {this.tabs.map((tab, index) => { - if (!tab.onlyAgent || (tab.onlyAgent && this.props.agent)) { - return this.onSelectedTabChanged(tab.id)} - isSelected={selectView === tab.id} - key={index} - > - {tab.name} - - } - } - )} - - - ); - } + renderTabs(agent = false) { + const { selectView } = this.state; + if (!agent) { + } + return ( + + + {this.tabs.map((tab, index) => { + if (!tab.onlyAgent || (tab.onlyAgent && this.props.agent)) { + return ( + this.onSelectedTabChanged(tab.id)} + isSelected={selectView === tab.id} + key={index} + > + {tab.name} + + ); + } + })} + + + ); + } - startVis2PngByAgent = async () => { - const agent = - (this.props.agent || store.getState().appStateReducers.currentAgentData || {}).id || false; - await this.reportingService.startVis2Png(this.props.section, agent); - }; + startVis2PngByAgent = async () => { + const agent = + (this.props.agent || store.getState().appStateReducers.currentAgentData || {}).id || false; + await this.reportingService.startVis2Png(this.props.section, agent); + }; - async startReport() { - try { - this.setState({ loadingReport: true }); - const isDarkModeTheme = getUiSettings().get('theme:darkMode'); - if (isDarkModeTheme) { - const defaultTextColor = '#DFE5EF'; - try { - $('.euiButtonEmpty__text').css('color', 'black'); + async startReport() { + try { + this.setState({ loadingReport: true }); + const isDarkModeTheme = getUiSettings().get('theme:darkMode'); + if (isDarkModeTheme) { + const defaultTextColor = '#DFE5EF'; + try { + $('.euiButtonEmpty__text').css('color', 'black'); + await this.startVis2PngByAgent(); + $('.euiButtonEmpty__text').css('color', defaultTextColor); + } catch (e) { + $('.euiButtonEmpty__text').css('color', defaultTextColor); + this.setState({ loadingReport: false }); + } + } else { await this.startVis2PngByAgent(); - $('.euiButtonEmpty__text').css('color', defaultTextColor); - } catch (e) { - $('.euiButtonEmpty__text').css('color', defaultTextColor); - this.setState({ loadingReport: false }); } - } else { - await this.startVis2PngByAgent(); + } finally { + this.setState({ loadingReport: false }); } - } finally { - this.setState({ loadingReport: false }); } - } - renderReportButton() { - return ( - (this.props.disabledReport && - - - this.startReport()}> - Generate report + renderReportButton() { + return ( + (this.props.disabledReport && ( + + + this.startReport()} + > + Generate report - - - - || ( + + + )) || ( this.startReport()}> + onClick={async () => this.startReport()} + > Generate report - )) - ); - } + + ) + ); + } - renderDashboardButton() { - const href = `#/overview?tab=${this.props.section}&agentId=${this.props.agent.id}` - return ( - - this.onSelectedTabChanged('dashboard')}> - Dashboard + renderDashboardButton() { + const href = `#/overview?tab=${this.props.section}&agentId=${this.props.agent.id}`; + return ( + + this.onSelectedTabChanged('dashboard')} + > + Dashboard - - ); - } + + ); + } - renderSettingsButton() { - return ( - - this.onSelectedTabChanged('settings')}> - Configuration + renderSettingsButton() { + return ( + + this.onSelectedTabChanged('settings')} + > + Configuration - - ); - } + + ); + } - loadSection(id) { - this.setState({ selectView: id }); - } + loadSection(id) { + this.setState({ selectView: id }); + } - onSelectedTabChanged(id) { - if (id !== this.state.selectView) { - if (id === 'events' || id === 'dashboard' || id === 'inventory') { - this.$rootScope.moduleDiscoverReady = false; - if (this.props.switchSubTab) this.props.switchSubTab(id === 'events' ? 'discover' : id === 'inventory' ? 'inventory' : 'panels') - window.location.href = window.location.href.replace( - new RegExp("tabView=" + "[^\&]*"), - `tabView=${id === 'events' ? 'discover' : id === 'inventory' ? 'inventory' : 'panels'}`); - this.afterLoad = id; - this.loadSection('loader'); - } else { - this.loadSection(id === 'panels' ? 'dashboard' : id === 'discover' ? 'events' : id); + onSelectedTabChanged(id) { + if (id !== this.state.selectView) { + if (id === 'events' || id === 'dashboard' || id === 'inventory') { + this.$rootScope.moduleDiscoverReady = false; + if (this.props.switchSubTab) + this.props.switchSubTab( + id === 'events' ? 'discover' : id === 'inventory' ? 'inventory' : 'panels' + ); + window.location.href = window.location.href.replace( + new RegExp('tabView=' + '[^&]*'), + `tabView=${id === 'events' ? 'discover' : id === 'inventory' ? 'inventory' : 'panels'}` + ); + this.afterLoad = id; + this.loadSection('loader'); + } else { + this.loadSection(id === 'panels' ? 'dashboard' : id === 'discover' ? 'events' : id); + } } } - } - render() { - const { agent } = this.props; - const { selectView } = this.state; - const mainProps = { - selectView, - afterLoad: this.afterLoad, - buttons: this.buttons, - tabs: this.tabs, - renderTabs: () => this.renderTabs(), - renderReportButton: () => this.renderReportButton(), - renderDashboardButton: () => this.renderDashboardButton(), - renderSettingsButton: () => this.renderSettingsButton(), - loadSection: (id) => this.loadSection(id), - onSelectedTabChanged: (id) => this.onSelectedTabChanged(id) + render() { + const { agent } = this.props; + const { selectView } = this.state; + const mainProps = { + selectView, + afterLoad: this.afterLoad, + buttons: this.buttons, + tabs: this.tabs, + renderTabs: () => this.renderTabs(), + renderReportButton: () => this.renderReportButton(), + renderDashboardButton: () => this.renderDashboardButton(), + renderSettingsButton: () => this.renderSettingsButton(), + loadSection: (id) => this.loadSection(id), + onSelectedTabChanged: (id) => this.onSelectedTabChanged(id), + }; + return ( + <> + {(agent && ) || + (this.props.section && this.props.section !== 'welcome' && ( + + ))} + + ); } - return ( - <> - {agent && - - || ((this.props.section && this.props.section !== 'welcome') && - ) - } - - ); } -}) +); diff --git a/public/components/common/permissions/prompt.tsx b/public/components/common/permissions/prompt.tsx index 4ef609b54f..37e5747059 100644 --- a/public/components/common/permissions/prompt.tsx +++ b/public/components/common/permissions/prompt.tsx @@ -11,53 +11,79 @@ */ import React, { Fragment } from 'react'; -import { useUserPermissionsRequirements } from '../hooks/useUserPermissions'; -import { useUserRolesRequirements } from '../hooks/useUserRoles'; -import { EuiEmptyPrompt, EuiSpacer, EuiPanel } from '@elastic/eui'; -import { TUserPermissions, TUserPermissionsFunction, TUserRoles, TUserRolesFunction } from '../permissions/button'; +import { useUserPermissionsRequirements, useUserRolesRequirements } from '../hooks'; +import { EuiEmptyPrompt, EuiSpacer } from '@elastic/eui'; +import { + TUserPermissions, + TUserPermissionsFunction, + TUserRoles, + TUserRolesFunction, +} from '../permissions/button'; import { WzPermissionsFormatted } from './format'; -import { withErrorBoundary } from '../hocs'; +import { withErrorBoundary } from '../hocs/error-boundary/with-error-boundary'; -interface IEmptyPromptNoPermissions{ - permissions?: TUserPermissions - roles?: TUserRoles - actions?: React.ReactNode +interface IEmptyPromptNoPermissions { + permissions?: TUserPermissions; + roles?: TUserRoles; + actions?: React.ReactNode; } - -export const WzEmptyPromptNoPermissions = withErrorBoundary (({permissions, roles, actions}: IEmptyPromptNoPermissions) => { - return You have no permissions} - body={ - - {permissions && ( -
- This section requires the {permissions.length > 1 ? 'permissions' : 'permission'}: - {WzPermissionsFormatted(permissions)} -
- )} - {permissions && roles && ()} - {roles && ( -
- This section requires {roles.map(role => ({role})).reduce((accum, cur) => [accum, ', ', cur])} {roles.length > 1 ? 'roles' : 'role'} -
- )} -
- } - actions={actions} - /> -}); - -interface IPromptNoPermissions{ - permissions?: TUserPermissions | TUserPermissionsFunction - roles?: TUserRoles | TUserRolesFunction - children?: React.ReactNode - rest?: any +export const WzEmptyPromptNoPermissions = withErrorBoundary( + ({ permissions, roles, actions }: IEmptyPromptNoPermissions) => { + return ( + You have no permissions} + body={ + + {permissions && ( +
+ This section requires the {permissions.length > 1 ? 'permissions' : 'permission'}: + {WzPermissionsFormatted(permissions)} +
+ )} + {permissions && roles && } + {roles && ( +
+ This section requires{' '} + {roles + .map((role) => {role}) + .reduce((accum, cur) => [accum, ', ', cur])}{' '} + {roles.length > 1 ? 'roles' : 'role'} +
+ )} +
+ } + actions={actions} + /> + ); + } +); +interface IPromptNoPermissions { + permissions?: TUserPermissions | TUserPermissionsFunction; + roles?: TUserRoles | TUserRolesFunction; + children?: React.ReactNode; + rest?: any; } -export const WzPromptPermissions = ({permissions = null, roles = null, children, ...rest} : IPromptNoPermissions) => { - const [userPermissionRequirements, userPermissions] = useUserPermissionsRequirements(typeof permissions === 'function' ? permissions(rest) : permissions); - const [userRolesRequirements, userRoles] = useUserRolesRequirements(typeof roles === 'function' ? roles(rest) : roles); +export const WzPromptPermissions = ({ + permissions = null, + roles = null, + children, + ...rest +}: IPromptNoPermissions) => { + const [userPermissionRequirements, userPermissions] = useUserPermissionsRequirements( + typeof permissions === 'function' ? permissions(rest) : permissions + ); + const [userRolesRequirements, userRoles] = useUserRolesRequirements( + typeof roles === 'function' ? roles(rest) : roles + ); - return (userPermissionRequirements || userRolesRequirements) ? : children; -} \ No newline at end of file + return userPermissionRequirements || userRolesRequirements ? ( + + ) : ( + children + ); +}; diff --git a/public/components/common/welcome/agents-welcome.js b/public/components/common/welcome/agents-welcome.js index ac768905dc..044d83aa45 100644 --- a/public/components/common/welcome/agents-welcome.js +++ b/public/components/common/welcome/agents-welcome.js @@ -327,8 +327,7 @@ export const AgentsWelcome = withErrorBoundary (class AgentsWelcome extends Comp - );withErrorBoundary - + ); } buildTabCard(tab, icon) { diff --git a/public/components/management/cluster/cluster-visualization.js b/public/components/management/cluster/cluster-visualization.js index 231d77db56..7a961cf826 100644 --- a/public/components/management/cluster/cluster-visualization.js +++ b/public/components/management/cluster/cluster-visualization.js @@ -1,14 +1,15 @@ import React from 'react'; import KibanaVis from '../../../kibana-integrations/kibana-vis'; import { withErrorBoundary, withReduxProvider } from '../../../components/common/hocs'; -import {compose} from 'redux' +import { compose } from 'redux'; -function KibanaVisClass(props) { +export const KibanaVisWrapper = compose( + withErrorBoundary, + withReduxProvider +)((props) => { return (
); -} - -export const KibanaVisWrapper = compose(withErrorBoundary, withReduxProvider)(KibanaVisClass); \ No newline at end of file +}); \ No newline at end of file diff --git a/public/components/wz-agent-selector/wz-agent-selector-wrapper.js b/public/components/wz-agent-selector/wz-agent-selector-wrapper.js index 9bef47cc22..290e54efdf 100644 --- a/public/components/wz-agent-selector/wz-agent-selector-wrapper.js +++ b/public/components/wz-agent-selector/wz-agent-selector-wrapper.js @@ -15,4 +15,7 @@ import WzAgentSelector from './wz-agent-selector'; import { compose } from 'redux'; import { withErrorBoundary, withReduxProvider } from '../common/hocs'; -export const WzAgentSelectorWrapper = compose (withErrorBoundary, withReduxProvider)(WzAgentSelector); \ No newline at end of file +export const WzAgentSelectorWrapper = compose( + withErrorBoundary, + withReduxProvider +)(WzAgentSelector); \ No newline at end of file From f227ee35b9079567ad25eff9a6992ee9d77ea8cc Mon Sep 17 00:00:00 2001 From: pablomarga Date: Tue, 15 Jun 2021 14:39:56 +0200 Subject: [PATCH 24/24] Added changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c8bac1daf..6dae2bc5b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ All notable changes to the Wazuh app project will be documented in this file. - Added new endpoint service to collect the frontend logs into a file [#3324](https://github.com/wazuh/wazuh-kibana-app/pull/3324) - Added new error handler to be responsible for the error orchestration [#3327](https://github.com/wazuh/wazuh-kibana-app/pull/3327) - Added `Error Boundary` HOC and Component to handle render errors. [#3321](https://github.com/wazuh/wazuh-kibana-app/pull/3321) +- Implemented `Error Boundary` HOC in each main react-component. [#3367](https://github.com/wazuh/wazuh-kibana-app/pull/3367) ### Changed