[ResponseOps] Bring back EuiThemeProvider to fix o11y and stack monitoring rules in stack management (#188724)

Related to https://github.com/elastic/kibana/issues/186969 and
elastic/response-ops-team#218

## Summary

This PR brings back EuiThemeProvider to fix o11y and stack monitoring
rules in stack management.

## To check/do

- [x] Add an APM test that fails without this fix
- [x] Check if this solves the related SDH for [CPU
Usage](https://github.com/elastic/sdh-kibana/issues/4829)
- Yes, it will solve that issue
([comment](https://github.com/elastic/sdh-kibana/issues/4829#issuecomment-2242509680))
- [x] Smoke test **ALL** rule types in stack management
    - Can we load the rule form?
    - Can we adjust all the input fields without error?
    - Does it work both with and without data in the preview chart?
This commit is contained in:
Maryam Saeidi 2024-07-23 10:42:42 +02:00 committed by GitHub
parent 11e81d15ec
commit ed32c98072
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 107 additions and 22 deletions

View file

@ -14,10 +14,11 @@ interface Props {
value: React.ReactNode; value: React.ReactNode;
children?: React.ReactNode; children?: React.ReactNode;
color?: ExpressionColor; color?: ExpressionColor;
dataTestSubj?: string;
} }
export function PopoverExpression(props: Props) { export function PopoverExpression(props: Props) {
const { title, value, children, color } = props; const { title, value, children, color, dataTestSubj } = props;
const [popoverOpen, setPopoverOpen] = useState(false); const [popoverOpen, setPopoverOpen] = useState(false);
return ( return (
@ -27,6 +28,7 @@ export function PopoverExpression(props: Props) {
closePopover={() => setPopoverOpen(false)} closePopover={() => setPopoverOpen(false)}
button={ button={
<EuiExpression <EuiExpression
data-test-subj={dataTestSubj}
description={title} description={title}
value={value} value={value}
isActive={popoverOpen} isActive={popoverOpen}

View file

@ -34,6 +34,7 @@ export function ServiceField({
}) { }) {
return ( return (
<PopoverExpression <PopoverExpression
dataTestSubj="apmServiceField"
value={currentValue || allOptionText} value={currentValue || allOptionText}
title={i18n.translate('xpack.apm.alerting.fields.service', { title={i18n.translate('xpack.apm.alerting.fields.service', {
defaultMessage: 'Service', defaultMessage: 'Service',
@ -68,6 +69,7 @@ export function EnvironmentField({
}) { }) {
return ( return (
<PopoverExpression <PopoverExpression
dataTestSubj="apmEnvironmentField"
value={getEnvironmentLabel(currentValue)} value={getEnvironmentLabel(currentValue)}
title={i18n.translate('xpack.apm.alerting.fields.environment', { title={i18n.translate('xpack.apm.alerting.fields.environment', {
defaultMessage: 'Environment', defaultMessage: 'Environment',
@ -106,7 +108,11 @@ export function TransactionNameField({
}); });
return ( return (
<PopoverExpression value={currentValue || allOptionText} title={label}> <PopoverExpression
dataTestSubj="apmTransactionNameField"
value={currentValue || allOptionText}
title={label}
>
<SuggestionsSelect <SuggestionsSelect
customOptions={[{ label: allOptionText, value: undefined }]} customOptions={[{ label: allOptionText, value: undefined }]}
customOptionText={i18n.translate('xpack.apm.alerting.transaction.name.custom.text', { customOptionText={i18n.translate('xpack.apm.alerting.transaction.name.custom.text', {
@ -139,7 +145,11 @@ export function TransactionTypeField({
defaultMessage: 'Type', defaultMessage: 'Type',
}); });
return ( return (
<PopoverExpression value={currentValue || allOptionText} title={label}> <PopoverExpression
dataTestSubj="apmTransactionTypeField"
value={currentValue || allOptionText}
title={label}
>
<SuggestionsSelect <SuggestionsSelect
customOptions={[{ label: allOptionText, value: undefined }]} customOptions={[{ label: allOptionText, value: undefined }]}
customOptionText={i18n.translate('xpack.apm.transactionTypesSelectCustomOptionText', { customOptionText={i18n.translate('xpack.apm.transactionTypesSelectCustomOptionText', {
@ -172,7 +182,11 @@ export function ErrorGroupingKeyField({
defaultMessage: 'Error grouping key', defaultMessage: 'Error grouping key',
}); });
return ( return (
<PopoverExpression value={currentValue || allOptionText} title={label}> <PopoverExpression
dataTestSubj="apmErrorGroupingKeyField"
value={currentValue || allOptionText}
title={label}
>
<SuggestionsSelect <SuggestionsSelect
customOptions={[{ label: allOptionText, value: undefined }]} customOptions={[{ label: allOptionText, value: undefined }]}
customOptionText={i18n.translate('xpack.apm.errorKeySelectCustomOptionText', { customOptionText={i18n.translate('xpack.apm.errorKeySelectCustomOptionText', {

View file

@ -43,7 +43,10 @@ import {
EuiToolTip, EuiToolTip,
EuiCallOut, EuiCallOut,
EuiAccordion, EuiAccordion,
useEuiTheme,
COLOR_MODES_STANDARD,
} from '@elastic/eui'; } from '@elastic/eui';
import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common';
import { capitalize } from 'lodash'; import { capitalize } from 'lodash';
import { KibanaFeature } from '@kbn/features-plugin/public'; import { KibanaFeature } from '@kbn/features-plugin/public';
import { import {
@ -199,6 +202,7 @@ export const RuleForm = ({
dataViews, dataViews,
} = useKibana().services; } = useKibana().services;
const canShowActions = hasShowActionsCapability(capabilities); const canShowActions = hasShowActionsCapability(capabilities);
const { colorMode } = useEuiTheme();
const [ruleTypeModel, setRuleTypeModel] = useState<RuleTypeModel | null>(null); const [ruleTypeModel, setRuleTypeModel] = useState<RuleTypeModel | null>(null);
const flyoutBodyOverflowRef = useRef<HTMLDivElement | HTMLSpanElement | null>(null); const flyoutBodyOverflowRef = useRef<HTMLDivElement | HTMLSpanElement | null>(null);
@ -765,24 +769,26 @@ export const RuleForm = ({
</SectionLoading> </SectionLoading>
} }
> >
<RuleParamsExpressionComponent <EuiThemeProvider darkMode={colorMode === COLOR_MODES_STANDARD.dark}>
id={rule.id} <RuleParamsExpressionComponent
ruleParams={rule.params} id={rule.id}
ruleInterval={`${ruleInterval ?? 1}${ruleIntervalUnit}`} ruleParams={rule.params}
ruleThrottle={''} ruleInterval={`${ruleInterval ?? 1}${ruleIntervalUnit}`}
alertNotifyWhen={rule.notifyWhen ?? 'onActionGroupChange'} ruleThrottle={''}
errors={errors} alertNotifyWhen={rule.notifyWhen ?? 'onActionGroupChange'}
setRuleParams={setRuleParams} errors={errors}
setRuleProperty={setRuleProperty} setRuleParams={setRuleParams}
defaultActionGroupId={defaultActionGroupId} setRuleProperty={setRuleProperty}
actionGroups={selectedRuleType.actionGroups} defaultActionGroupId={defaultActionGroupId}
metadata={metadata} actionGroups={selectedRuleType.actionGroups}
charts={charts} metadata={metadata}
data={data} charts={charts}
dataViews={dataViews} data={data}
unifiedSearch={unifiedSearch} dataViews={dataViews}
onChangeMetaData={onChangeMetaData} unifiedSearch={unifiedSearch}
/> onChangeMetaData={onChangeMetaData}
/>
</EuiThemeProvider>
</Suspense> </Suspense>
</EuiErrorBoundary> </EuiErrorBoundary>
) : null} ) : null}

View file

@ -8,6 +8,9 @@
import expect from '@kbn/expect'; import expect from '@kbn/expect';
import { asyncForEach } from '@kbn/std'; import { asyncForEach } from '@kbn/std';
import { omit } from 'lodash'; import { omit } from 'lodash';
import { apm, timerange } from '@kbn/apm-synthtrace-client';
import { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace';
import { getApmSynthtraceEsClient } from '../../../common/utils/synthtrace/apm_es_client';
import { FtrProviderContext } from '../../ftr_provider_context'; import { FtrProviderContext } from '../../ftr_provider_context';
import { generateUniqueKey } from '../../lib/get_test_data'; import { generateUniqueKey } from '../../lib/get_test_data';
@ -20,6 +23,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
const retry = getService('retry'); const retry = getService('retry');
const rules = getService('rules'); const rules = getService('rules');
const toasts = getService('toasts'); const toasts = getService('toasts');
const esClient = getService('es');
const apmSynthtraceKibanaClient = getService('apmSynthtraceKibanaClient');
async function getAlertsByName(name: string) { async function getAlertsByName(name: string) {
const { const {
@ -71,6 +76,12 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
await nameInput.click(); await nameInput.click();
} }
async function defineAPMErrorCountRule(ruleName: string) {
await pageObjects.triggersActionsUI.clickCreateAlertButton();
await testSubjects.click(`apm.error_rate-SelectOption`);
await testSubjects.setValue('ruleNameInput', ruleName);
}
async function defineAlwaysFiringAlert(alertName: string) { async function defineAlwaysFiringAlert(alertName: string) {
await pageObjects.triggersActionsUI.clickCreateAlertButton(); await pageObjects.triggersActionsUI.clickCreateAlertButton();
await testSubjects.click('test.always-firing-SelectOption'); await testSubjects.click('test.always-firing-SelectOption');
@ -82,6 +93,45 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
} }
describe('create alert', function () { describe('create alert', function () {
let apmSynthtraceEsClient: ApmSynthtraceEsClient;
before(async () => {
const version = (await apmSynthtraceKibanaClient.installApmPackage()).version;
apmSynthtraceEsClient = await getApmSynthtraceEsClient({
client: esClient,
packageVersion: version,
});
const opbeansJava = apm
.service({ name: 'opbeans-java', environment: 'production', agentName: 'java' })
.instance('instance');
const opbeansNode = apm
.service({ name: 'opbeans-node', environment: 'production', agentName: 'node' })
.instance('instance');
const events = timerange('now-15m', 'now')
.ratePerMinute(1)
.generator((timestamp) => {
return [
opbeansJava
.transaction({ transactionName: 'tx-java' })
.timestamp(timestamp)
.duration(100)
.failure()
.errors(opbeansJava.error({ message: 'a java error' }).timestamp(timestamp + 50)),
opbeansNode
.transaction({ transactionName: 'tx-node' })
.timestamp(timestamp)
.duration(100)
.success(),
];
});
return Promise.all([apmSynthtraceEsClient.index(events)]);
});
after(() => apmSynthtraceEsClient.clean());
beforeEach(async () => { beforeEach(async () => {
await pageObjects.common.navigateToApp('triggersActions'); await pageObjects.common.navigateToApp('triggersActions');
await testSubjects.click('rulesTab'); await testSubjects.click('rulesTab');
@ -321,6 +371,19 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
await discardNewRuleCreation(); await discardNewRuleCreation();
}); });
// Related issue that this test is trying to prevent:
// https://github.com/elastic/kibana/issues/186969
it('should successfully show the APM error count rule flyout', async () => {
const ruleName = generateUniqueKey();
await defineAPMErrorCountRule(ruleName);
await testSubjects.existOrFail('apmServiceField');
await testSubjects.existOrFail('apmEnvironmentField');
await testSubjects.existOrFail('apmErrorGroupingKeyField');
await discardNewRuleCreation();
});
it('should successfully test valid es_query alert', async () => { it('should successfully test valid es_query alert', async () => {
const alertName = generateUniqueKey(); const alertName = generateUniqueKey();
await defineEsQueryAlert(alertName); await defineEsQueryAlert(alertName);