Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add frontend state service #7037

Open
wants to merge 32 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
c289b6b
feat(state): create state core service
Desvelao Sep 30, 2024
4328609
chore: remove unused methods and no-effect functions
Desvelao Sep 30, 2024
bf5a9ab
feat(state): add tests
Desvelao Sep 30, 2024
f2e54f6
feat(state): wrap the subscrition handler to allow they can unsubscri…
Desvelao Oct 1, 2024
ffea2e3
Merge branch 'master' of https://github.com/wazuh/wazuh-kibana-app in…
Desvelao Nov 27, 2024
f11467b
fix(state): enhance README.md examples
Desvelao Nov 27, 2024
d5dd4bb
Apply suggestions from code review
Desvelao Nov 27, 2024
05cfa5e
fix(state): typos
Desvelao Nov 27, 2024
722dfef
Merge branch 'enhancement/6959-extract-common-services-frontend-state…
Desvelao Nov 27, 2024
d86df0c
fix(state): minor fixes
Desvelao Nov 27, 2024
b883a65
fix(state): unsubscribe from state container
Desvelao Nov 27, 2024
01fc56f
fix: upgrade react-cookie dependency to remove vulnerability in package
Desvelao Nov 27, 2024
f6e7854
Apply suggestions from code review
Desvelao Nov 27, 2024
65d7a4c
Merge branch 'master' of https://github.com/wazuh/wazuh-kibana-app in…
Desvelao Dec 11, 2024
9a2dafa
fix(state): lint and prettier
Desvelao Dec 12, 2024
d45a4c3
fix(state): lint and prettier
Desvelao Dec 12, 2024
ad1f0d2
fix(core): dependencies
Desvelao Dec 12, 2024
f03fca3
fix(settings): improve types for SettingsComponent props
guidomodarelli Dec 12, 2024
56395ee
fix(eslint): disable unicorn/no-static-only-class rule
guidomodarelli Dec 12, 2024
93eedab
fix(types): improve type annotations for decoratorCheckIsEnabled call…
guidomodarelli Dec 12, 2024
cc66b04
fix(eslint): remove redundant eslint directive for unicorn/no-static-…
guidomodarelli Dec 12, 2024
f6c2ff4
fix(types): set default type parameters for State and LifecycleServic…
guidomodarelli Dec 12, 2024
3fde6b8
fix(eslint): remove unnecessary eslint directive for unicorn/no-stati…
guidomodarelli Dec 12, 2024
254fcdd
fix(eslint): add consistent-function-scoping rule to enforce function…
guidomodarelli Dec 12, 2024
c8bc275
fix(eslint): replace Promise.reject with throw for error handling in …
guidomodarelli Dec 12, 2024
ee2a167
fix(eslint): update type definition for WrappedComponent in createHOC…
guidomodarelli Dec 12, 2024
1f1caa9
fix(types): update remove method type in StateContainer for improved …
guidomodarelli Dec 12, 2024
7a2c1f2
fix(types): update createHooks to handle optional updater$ for better…
guidomodarelli Dec 12, 2024
5a9c0de
fix(logging): improve error handling by explicitly casting error to E…
guidomodarelli Dec 12, 2024
ecd91a9
fix(logging): enhance error logging by explicitly casting to Error fo…
guidomodarelli Dec 12, 2024
e29fb5b
fix(types): improve typing for set method and enhance error logging c…
guidomodarelli Dec 12, 2024
9da059b
Merge branch 'master' into enhancement/6959-extract-common-services-f…
guidomodarelli Dec 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,13 @@ module.exports = {
/* -------------------------------------------------------------------------- */
/* unicorn */
/* -------------------------------------------------------------------------- */
'unicorn/consistent-function-scoping': [
'error',
{
checkArrowFunctions: false,
},
],
'unicorn/no-static-only-class': 'off',
'unicorn/prefer-module': 'off',
'unicorn/prefer-ternary': 'off',
// https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/custom-error-definition.md
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,24 +53,22 @@ jest.mock('../../../components/common/hooks', () => ({
}));

jest.mock('../services', () => ({
checkPatternService: appInfo => () => undefined,
checkTemplateService: appInfo => () => undefined,
checkApiService: appInfo => () => undefined,
checkSetupService: appInfo => () => undefined,
checkFieldsService: appInfo => () => undefined,
checkPluginPlatformSettings: appInfo => () => undefined,
checkPatternSupportService: appInfo => () => undefined,
checkIndexPatternService: appInfo => () => undefined,
checkPatternService: appInfo => () => {},
checkTemplateService: appInfo => () => {},
checkApiService: appInfo => () => {},
checkSetupService: appInfo => () => {},
checkFieldsService: appInfo => () => {},
checkPluginPlatformSettings: appInfo => () => {},
checkPatternSupportService: appInfo => () => {},
checkIndexPatternService: appInfo => () => {},
}));

jest.mock('../components/check-result', () => ({
CheckResult: () => <></>,
}));

jest.mock('../../../react-services', () => ({
AppState: {
setPatternSelector: () => {},
},
AppState: {},
ErrorHandler: {
handle: error => error,
},
Expand Down Expand Up @@ -123,6 +121,7 @@ describe('Health Check container', () => {
]); // invoke is wrapped with act to await for setState

const callOutError = component.find('EuiCallOut');

expect(callOutError.text()).toBe('[API version] Test error');
});

Expand All @@ -134,6 +133,7 @@ describe('Health Check container', () => {
]);

const callOutWarning = component.find('EuiCallOut');

expect(callOutWarning.text()).toBe('[API version] Test warning');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ import {
EuiCallOut,
EuiDescriptionList,
EuiSpacer,
EuiFlexGroup,
EuiFlexItem,
} from '@elastic/eui';
import React, { Fragment, useState, useEffect, useRef } from 'react';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { AppState, ErrorHandler } from '../../../react-services';
import { compose } from 'redux';
import { ErrorHandler } from '../../../react-services';
import {
useAppConfig,
useRouterSearch,
Expand All @@ -33,7 +35,7 @@ import {
} from '../services';
import { CheckResult } from '../components/check-result';
import { withErrorBoundary, withRouteResolvers } from '../../common/hocs';
import { getCore, getHttp, getWzCurrentAppID } from '../../../kibana-services';
import { getCore, getHttp } from '../../../kibana-services';
import {
HEALTH_CHECK_REDIRECTION_TIME,
WAZUH_INDEX_TYPE_MONITORING,
Expand All @@ -43,7 +45,6 @@ import { getThemeAssetURL, getAssetURL } from '../../../utils/assets';
import { serverApis } from '../../../utils/applications';
import { RedirectAppLinks } from '../../../../../../src/plugins/opensearch_dashboards_react/public';
import { ip, wzConfig } from '../../../services/resolves';
import { compose } from 'redux';
import NavigationService from '../../../react-services/navigation-service';

const checks = {
Expand Down Expand Up @@ -94,12 +95,51 @@ const checks = {
},
};

const addTagsToUrl = (error: string) => {
const words = error.split(' ');

for (const [index, word] of words.entries()) {
if (word.includes('http://') || word.includes('https://')) {
if (words[index - 1] === 'guide:') {
if (word.endsWith('.') || word.endsWith(',')) {
words[index - 2] = `<a href="${word.slice(
0,
-1,
)}" rel="noopener noreferrer" target="_blank">${
words[index - 2]
} ${words[index - 1].slice(0, -1)}</a>${word.slice(-1)}`;
} else {
words[index - 2] =
`<a href="${word}" rel="noopener noreferrer" target="_blank">${
words[index - 2]
} ${words[index - 1].slice(0, -1)}</a> `;
}

words.splice(index - 1, 2);
} else {
if (word.endsWith('.') || word.endsWith(',')) {
words[index] = `<a href="${word.slice(
0,
-1,
)}" rel="noopener noreferrer" target="_blank">${word.slice(
0,
-1,
)}</a>${word.slice(-1)}`;
} else {
words[index] =
`<a href="${word}" rel="noopener noreferrer" target="_blank">${word}</a>`;
}
}
}
}

return words.join(' ');
};

function HealthCheckComponent() {
const [checkWarnings, setCheckWarnings] = useState<{ [key: string]: [] }>({});
const [checkErrors, setCheckErrors] = useState<{ [key: string]: [] }>({});
const [checksReady, setChecksReady] = useState<{ [key: string]: boolean }>(
{},
);
const [checkWarnings, setCheckWarnings] = useState<Record<string, []>>({});
const [checkErrors, setCheckErrors] = useState<Record<string, []>>({});
const [checksReady, setChecksReady] = useState<Record<string, boolean>>({});
const [isDebugMode, setIsDebugMode] = useState<boolean>(false);
const appConfig = useAppConfig();
const checksInitiated = useRef(false);
Expand All @@ -119,6 +159,7 @@ function HealthCheckComponent() {
.pathname +
'?' +
searchParams;

NavigationService.getInstance().navigate(relativePath);
} else {
NavigationService.getInstance().navigate('/');
Expand All @@ -131,25 +172,23 @@ function HealthCheckComponent() {
useEffect(() => {
if (appConfig.isReady && !checksInitiated.current) {
checksInitiated.current = true;
AppState.setPatternSelector(appConfig.data['ip.selector']);
}
}, [appConfig]);

useEffect(() => {
// Redirect to app when all checks are ready
Object.keys(checks).every(check => checksReady[check]) &&
if (
Object.keys(checks).every(check => checksReady[check]) &&
!isDebugMode &&
!thereAreWarnings &&
(() =>
setTimeout(
redirectionPassHealthcheck,
HEALTH_CHECK_REDIRECTION_TIME,
))();
!thereAreWarnings
) {
setTimeout(redirectionPassHealthcheck, HEALTH_CHECK_REDIRECTION_TIME);
}
}, [checksReady]);

useEffect(() => {
// Check if Health should not redirect automatically (Debug mode)
setIsDebugMode(typeof search.debug !== 'undefined');
setIsDebugMode(search.debug !== undefined);
}, []);

const handleWarnings = (checkID, warnings, parsed) => {
Expand All @@ -161,6 +200,7 @@ function HealthCheckComponent() {
}),
)
: warnings;

setCheckWarnings(prev => ({ ...prev, [checkID]: newWarnings }));
};

Expand All @@ -173,6 +213,7 @@ function HealthCheckComponent() {
}),
)
: errors;

setCheckErrors(prev => ({ ...prev, [checkID]: newErrors }));
};

Expand All @@ -199,73 +240,32 @@ function HealthCheckComponent() {

const renderChecks = () => {
const showLogButton = thereAreErrors || thereAreWarnings || isDebugMode;
return Object.keys(checks).map((check, index) => {
return (
<CheckResult
showLogButton={showLogButton}
key={`health_check_check_${check}`}
name={check}
title={checks[check].title}
awaitFor={checks[check].awaitFor}
shouldCheck={
checks[check].shouldCheck || appConfig.data[`checks.${check}`]
}
validationService={checks[check].validator(appConfig)}
handleWarnings={handleWarnings}
handleErrors={handleErrors}
cleanWarnings={cleanWarnings}
cleanErrors={cleanErrors}
isLoading={appConfig.isLoading}
handleCheckReady={handleCheckReady}
checksReady={checksReady}
canRetry={checks[check].canRetry}
/>
);
});
};

const addTagsToUrl = error => {
const words = error.split(' ');
words.forEach((word, index) => {
if (word.includes('http://') || word.includes('https://')) {
if (words[index - 1] === 'guide:') {
if (word.endsWith('.') || word.endsWith(',')) {
words[index - 2] = `<a href="${word.slice(
0,
-1,
)}" rel="noopener noreferrer" target="_blank">${
words[index - 2]
} ${words[index - 1].slice(0, -1)}</a>${word.slice(-1)}`;
} else {
words[
index - 2
] = `<a href="${word}" rel="noopener noreferrer" target="_blank">${
words[index - 2]
} ${words[index - 1].slice(0, -1)}</a> `;
}
words.splice(index - 1, 2);
} else {
if (word.endsWith('.') || word.endsWith(',')) {
words[index] = `<a href="${word.slice(
0,
-1,
)}" rel="noopener noreferrer" target="_blank">${word.slice(
0,
-1,
)}</a>${word.slice(-1)}`;
} else {
words[
index
] = `<a href="${word}" rel="noopener noreferrer" target="_blank">${word}</a>`;
}
return Object.keys(checks).map(check => (
<CheckResult
showLogButton={showLogButton}
key={`health_check_check_${check}`}
name={check}
title={checks[check].title}
awaitFor={checks[check].awaitFor}
shouldCheck={
checks[check].shouldCheck || appConfig.data[`checks.${check}`]
}
}
});
return words.join(' ');
validationService={checks[check].validator(appConfig)}
handleWarnings={handleWarnings}
handleErrors={handleErrors}
cleanWarnings={cleanWarnings}
cleanErrors={cleanErrors}
isLoading={appConfig.isLoading}
handleCheckReady={handleCheckReady}
checksReady={checksReady}
canRetry={checks[check].canRetry}
/>
));
};

const renderWarnings = () => {
return Object.keys(checkWarnings).map(checkID =>
const renderWarnings = () =>
Object.keys(checkWarnings).map(checkID =>
checkWarnings[checkID].map((warning, index) => (
<Fragment key={index}>
<EuiCallOut
Expand All @@ -286,10 +286,8 @@ function HealthCheckComponent() {
</Fragment>
)),
);
};

const renderErrors = () => {
return Object.keys(checkErrors).map(checkID =>
const renderErrors = () =>
Object.keys(checkErrors).map(checkID =>
checkErrors[checkID].map((error, index) => (
<Fragment key={index}>
<EuiCallOut
Expand All @@ -310,7 +308,6 @@ function HealthCheckComponent() {
</Fragment>
)),
);
};

return (
<div className='health-check'>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,12 +154,10 @@ export const PromptCheckIndex = (props: {
);
};

const mapStateToProps = state => {
return {
vulnerabilitiesStatesindexPatternID:
state.appConfig.data['vulnerabilities.pattern'],
};
};
const mapStateToProps = state => ({
vulnerabilitiesStatesindexPatternID:
state.appConfig.data['vulnerabilities.pattern'],
});

export const withVulnerabilitiesStateDataSource = compose(
connect(mapStateToProps),
Expand Down
Loading
Loading