mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
# Backport This will backport the following commits from `main` to `8.x`: - [[Streams 🌊] Update condition editor enabling and fixes (#218055)](https://github.com/elastic/kibana/pull/218055) <!--- Backport version: 9.6.6 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sorenlouv/backport) <!--BACKPORT [{"author":{"name":"Marco Antonio Ghiani","email":"marcoantonio.ghiani01@gmail.com"},"sourceCommit":{"committedDate":"2025-04-14T12:25:46Z","message":"[Streams 🌊] Update condition editor enabling and fixes (#218055)\n\n## 📓 Summary\n\nCloses #217884 \n\n- Updates the condition editor to have a more consistent behaviour for\nenabled/disabled routing.\n- A more explicit tooltip is added to describe how the status flag\naffects the routing behaviour.\n- The status switch is visible by default, while before it was shown\nonly in edit mode for a routing condition.\n - Fixed crashes when manually working on the syntax editor.\n- Removes the routing status flag from the condition editor in the\nprocessors' config.\n\n<img width=\"763\" alt=\"Screenshot 2025-04-14 at 10 23 42\"\nsrc=\"https://github.com/user-attachments/assets/8521739a-ac53-4751-9ad3-4400a84c5a8d\"\n/>","sha":"d46a89e4a547054077d31de1a4b281615734c6a4","branchLabelMapping":{"^v9.1.0$":"main","^v8.19.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","Team:obs-ux-logs","backport:version","Feature:Streams","v9.1.0","v8.19.0"],"title":"[Streams 🌊] Update condition editor enabling and fixes","number":218055,"url":"https://github.com/elastic/kibana/pull/218055","mergeCommit":{"message":"[Streams 🌊] Update condition editor enabling and fixes (#218055)\n\n## 📓 Summary\n\nCloses #217884 \n\n- Updates the condition editor to have a more consistent behaviour for\nenabled/disabled routing.\n- A more explicit tooltip is added to describe how the status flag\naffects the routing behaviour.\n- The status switch is visible by default, while before it was shown\nonly in edit mode for a routing condition.\n - Fixed crashes when manually working on the syntax editor.\n- Removes the routing status flag from the condition editor in the\nprocessors' config.\n\n<img width=\"763\" alt=\"Screenshot 2025-04-14 at 10 23 42\"\nsrc=\"https://github.com/user-attachments/assets/8521739a-ac53-4751-9ad3-4400a84c5a8d\"\n/>","sha":"d46a89e4a547054077d31de1a4b281615734c6a4"}},"sourceBranch":"main","suggestedTargetBranches":["8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.1.0","branchLabelMappingKey":"^v9.1.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/218055","number":218055,"mergeCommit":{"message":"[Streams 🌊] Update condition editor enabling and fixes (#218055)\n\n## 📓 Summary\n\nCloses #217884 \n\n- Updates the condition editor to have a more consistent behaviour for\nenabled/disabled routing.\n- A more explicit tooltip is added to describe how the status flag\naffects the routing behaviour.\n- The status switch is visible by default, while before it was shown\nonly in edit mode for a routing condition.\n - Fixed crashes when manually working on the syntax editor.\n- Removes the routing status flag from the condition editor in the\nprocessors' config.\n\n<img width=\"763\" alt=\"Screenshot 2025-04-14 at 10 23 42\"\nsrc=\"https://github.com/user-attachments/assets/8521739a-ac53-4751-9ad3-4400a84c5a8d\"\n/>","sha":"d46a89e4a547054077d31de1a4b281615734c6a4"}},{"branch":"8.x","label":"v8.19.0","branchLabelMappingKey":"^v8.19.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> Co-authored-by: Marco Antonio Ghiani <marcoantonio.ghiani01@gmail.com>
This commit is contained in:
parent
161e3d508f
commit
0c8a0b29db
8 changed files with 215 additions and 210 deletions
|
@ -5,153 +5,71 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import useToggle from 'react-use/lib/useToggle';
|
||||
import {
|
||||
EuiCodeBlock,
|
||||
EuiFieldText,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiForm,
|
||||
EuiFormRow,
|
||||
EuiIconTip,
|
||||
EuiSelect,
|
||||
EuiSelectOption,
|
||||
EuiSwitch,
|
||||
EuiText,
|
||||
EuiToolTip,
|
||||
} from '@elastic/eui';
|
||||
import {
|
||||
BinaryFilterCondition,
|
||||
Condition,
|
||||
FilterCondition,
|
||||
isCondition,
|
||||
isNeverCondition,
|
||||
} from '@kbn/streams-schema';
|
||||
import React, { useEffect } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { css } from '@emotion/css';
|
||||
import { CodeEditor } from '@kbn/code-editor';
|
||||
import { isPlainObject } from 'lodash';
|
||||
import {
|
||||
EMPTY_EQUALS_CONDITION,
|
||||
ALWAYS_CONDITION,
|
||||
NEVER_CONDITION,
|
||||
alwaysToEmptyEquals,
|
||||
emptyEqualsToAlways,
|
||||
} from '../../../util/condition';
|
||||
|
||||
export function ConditionEditor(props: {
|
||||
condition: Condition;
|
||||
onConditionChange?: (condition: Condition) => void;
|
||||
isNew?: boolean;
|
||||
}) {
|
||||
const normalizedCondition = alwaysToEmptyEquals(props.condition);
|
||||
export type RoutingConditionEditorProps = ConditionEditorProps;
|
||||
|
||||
const handleConditionChange = (condition: Condition) => {
|
||||
props.onConditionChange?.(emptyEqualsToAlways(condition));
|
||||
};
|
||||
export function RoutingConditionEditor(props: RoutingConditionEditorProps) {
|
||||
const isEnabled = !isNeverCondition(props.condition);
|
||||
|
||||
return (
|
||||
<ConditionForm
|
||||
condition={normalizedCondition}
|
||||
onConditionChange={handleConditionChange}
|
||||
isNew={props.isNew}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function ConditionForm(props: {
|
||||
condition: Condition;
|
||||
onConditionChange: (condition: Condition) => void;
|
||||
isNew?: boolean;
|
||||
}) {
|
||||
const [syntaxEditor, setSyntaxEditor] = React.useState(() =>
|
||||
Boolean(props.condition && !('operator' in props.condition))
|
||||
);
|
||||
const [jsonCondition, setJsonCondition] = React.useState<string | null>(() =>
|
||||
JSON.stringify(props.condition, null, 2)
|
||||
);
|
||||
useEffect(() => {
|
||||
if (!syntaxEditor && props.condition) {
|
||||
setJsonCondition(JSON.stringify(props.condition, null, 2));
|
||||
}
|
||||
}, [syntaxEditor, props.condition]);
|
||||
return (
|
||||
<EuiFlexGroup direction="column" gutterSize="s">
|
||||
{!props.isNew && (
|
||||
<>
|
||||
<EuiFlexItem grow>
|
||||
<EuiText
|
||||
className={css`
|
||||
font-weight: bold;
|
||||
`}
|
||||
size="xs"
|
||||
>
|
||||
{i18n.translate('xpack.streams.conditionEditor.title', { defaultMessage: 'Status' })}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiToolTip
|
||||
content={i18n.translate('xpack.streams.conditionEditor.disableTooltip', {
|
||||
defaultMessage: 'Route no documents to this stream without deleting existing data',
|
||||
<EuiForm fullWidth>
|
||||
<EuiFormRow
|
||||
label={
|
||||
<EuiFlexGroup gutterSize="xs" alignItems="center">
|
||||
{i18n.translate('xpack.streams.conditionEditor.title', {
|
||||
defaultMessage: 'Status',
|
||||
})}
|
||||
>
|
||||
<EuiSwitch
|
||||
label={i18n.translate('xpack.streams.conditionEditor.switch', {
|
||||
defaultMessage: 'Enabled',
|
||||
<EuiIconTip
|
||||
content={i18n.translate('xpack.streams.conditionEditor.disableTooltip', {
|
||||
defaultMessage:
|
||||
'When disabled, the routing rule stops sending documents to this stream. It does not remove existing data.',
|
||||
})}
|
||||
compressed
|
||||
checked={!isNeverCondition(props.condition)}
|
||||
onChange={() => {
|
||||
props.onConditionChange(
|
||||
isNeverCondition(props.condition) ? EMPTY_EQUALS_CONDITION : { never: {} }
|
||||
);
|
||||
setSyntaxEditor(false);
|
||||
}}
|
||||
/>
|
||||
</EuiToolTip>
|
||||
</>
|
||||
)}
|
||||
{(props.isNew || !isNeverCondition(props.condition)) && (
|
||||
<>
|
||||
<EuiFlexGroup alignItems="center" gutterSize="xs">
|
||||
<EuiFlexItem grow>
|
||||
<EuiText
|
||||
className={css`
|
||||
font-weight: bold;
|
||||
`}
|
||||
size="xs"
|
||||
>
|
||||
{i18n.translate('xpack.streams.conditionEditor.title', {
|
||||
defaultMessage: 'Condition',
|
||||
})}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiSwitch
|
||||
label={i18n.translate('xpack.streams.conditionEditor.switch', {
|
||||
defaultMessage: 'Syntax editor',
|
||||
})}
|
||||
compressed
|
||||
checked={syntaxEditor}
|
||||
onChange={() => setSyntaxEditor(!syntaxEditor)}
|
||||
/>
|
||||
</EuiFlexGroup>
|
||||
{syntaxEditor ? (
|
||||
<CodeEditor
|
||||
height={200}
|
||||
languageId="json"
|
||||
value={jsonCondition || ''}
|
||||
onChange={(e) => {
|
||||
setJsonCondition(e);
|
||||
try {
|
||||
const condition = JSON.parse(e);
|
||||
props.onConditionChange(condition);
|
||||
} catch (error: unknown) {
|
||||
// do nothing
|
||||
}
|
||||
}}
|
||||
/>
|
||||
) : !props.condition || 'operator' in props.condition ? (
|
||||
<FilterForm
|
||||
condition={(props.condition as FilterCondition) || EMPTY_EQUALS_CONDITION}
|
||||
onConditionChange={props.onConditionChange}
|
||||
/>
|
||||
) : (
|
||||
<pre>{JSON.stringify(props.condition, null, 2)}</pre>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
}
|
||||
>
|
||||
<EuiSwitch
|
||||
label={i18n.translate('xpack.streams.conditionEditor.switch', {
|
||||
defaultMessage: 'Enabled',
|
||||
})}
|
||||
compressed
|
||||
checked={isEnabled}
|
||||
onChange={(event) => {
|
||||
props.onConditionChange(event.target.checked ? ALWAYS_CONDITION : NEVER_CONDITION);
|
||||
}}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
{isEnabled && <ConditionEditor {...props} />}
|
||||
</EuiForm>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -173,77 +91,141 @@ const operatorMap = {
|
|||
notExists: i18n.translate('xpack.streams.filter.notExists', { defaultMessage: 'not exists' }),
|
||||
};
|
||||
|
||||
const operatorOptions: EuiSelectOption[] = Object.entries(operatorMap).map(([value, text]) => ({
|
||||
value,
|
||||
text,
|
||||
}));
|
||||
|
||||
export interface ConditionEditorProps {
|
||||
condition: Condition;
|
||||
onConditionChange: (condition: Condition) => void;
|
||||
}
|
||||
|
||||
export function ConditionEditor(props: ConditionEditorProps) {
|
||||
const isInvalidCondition = !isCondition(props.condition);
|
||||
|
||||
const condition = alwaysToEmptyEquals(props.condition);
|
||||
|
||||
const isFilterCondition = isPlainObject(condition) && 'operator' in condition;
|
||||
|
||||
const [usingSyntaxEditor, toggleSyntaxEditor] = useToggle(!isFilterCondition);
|
||||
|
||||
const handleConditionChange = (updatedCondition: Condition) => {
|
||||
props.onConditionChange(emptyEqualsToAlways(updatedCondition));
|
||||
};
|
||||
|
||||
return (
|
||||
<EuiFormRow
|
||||
label={i18n.translate('xpack.streams.conditionEditor.title', {
|
||||
defaultMessage: 'Condition',
|
||||
})}
|
||||
labelAppend={
|
||||
<EuiSwitch
|
||||
label={i18n.translate('xpack.streams.conditionEditor.switch', {
|
||||
defaultMessage: 'Syntax editor',
|
||||
})}
|
||||
compressed
|
||||
checked={usingSyntaxEditor}
|
||||
onChange={toggleSyntaxEditor}
|
||||
/>
|
||||
}
|
||||
isInvalid={isInvalidCondition}
|
||||
error={
|
||||
isInvalidCondition
|
||||
? i18n.translate('xpack.streams.conditionEditor.error', {
|
||||
defaultMessage: 'The condition is invalid or in unrecognized format.',
|
||||
})
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{usingSyntaxEditor ? (
|
||||
<CodeEditor
|
||||
height={200}
|
||||
languageId="json"
|
||||
value={JSON.stringify(condition, null, 2)}
|
||||
onChange={(value) => {
|
||||
try {
|
||||
handleConditionChange(JSON.parse(value));
|
||||
} catch (error: unknown) {
|
||||
// do nothing
|
||||
}
|
||||
}}
|
||||
/>
|
||||
) : isFilterCondition ? (
|
||||
<FilterForm condition={condition} onConditionChange={handleConditionChange} />
|
||||
) : (
|
||||
<EuiCodeBlock language="json" paddingSize="m" isCopyable>
|
||||
{JSON.stringify(condition, null, 2)}
|
||||
</EuiCodeBlock>
|
||||
)}
|
||||
</EuiFormRow>
|
||||
);
|
||||
}
|
||||
|
||||
function FilterForm(props: {
|
||||
condition: FilterCondition;
|
||||
onConditionChange: (condition: FilterCondition) => void;
|
||||
}) {
|
||||
const handleConditionChange = (updatedCondition: Partial<FilterCondition>) => {
|
||||
props.onConditionChange({
|
||||
...props.condition,
|
||||
...updatedCondition,
|
||||
} as FilterCondition);
|
||||
};
|
||||
|
||||
const handleOperatorChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
const newCondition: Partial<FilterCondition> = { ...props.condition };
|
||||
|
||||
const newOperator = event.target.value;
|
||||
if (newOperator === 'exists' || newOperator === 'notExists') {
|
||||
if ('value' in newCondition) delete newCondition.value;
|
||||
} else if (!('value' in newCondition)) {
|
||||
(newCondition as BinaryFilterCondition).value = '';
|
||||
}
|
||||
|
||||
props.onConditionChange({
|
||||
...newCondition,
|
||||
operator: newOperator,
|
||||
} as FilterCondition);
|
||||
};
|
||||
|
||||
return (
|
||||
<EuiFlexGroup gutterSize="s" alignItems="center">
|
||||
<EuiFlexItem grow>
|
||||
<EuiFieldText
|
||||
data-test-subj="streamsAppFilterFormFieldText"
|
||||
aria-label={i18n.translate('xpack.streams.filter.field', { defaultMessage: 'Field' })}
|
||||
compressed
|
||||
placeholder={i18n.translate('xpack.streams.filter.fieldPlaceholder', {
|
||||
defaultMessage: 'Field',
|
||||
})}
|
||||
value={props.condition.field}
|
||||
onChange={(e) => {
|
||||
props.onConditionChange({ ...props.condition, field: e.target.value });
|
||||
}}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow>
|
||||
<EuiSelect
|
||||
aria-label={i18n.translate('xpack.streams.filter.operator', {
|
||||
defaultMessage: 'Operator',
|
||||
})}
|
||||
data-test-subj="streamsAppFilterFormSelect"
|
||||
options={
|
||||
Object.entries(operatorMap).map(([value, text]) => ({
|
||||
value,
|
||||
text,
|
||||
})) as Array<{ value: FilterCondition['operator']; text: string }>
|
||||
}
|
||||
value={props.condition.operator}
|
||||
compressed
|
||||
onChange={(e) => {
|
||||
const newCondition: Partial<FilterCondition> = {
|
||||
...props.condition,
|
||||
};
|
||||
|
||||
const newOperator = e.target.value as FilterCondition['operator'];
|
||||
if (newOperator === 'exists' || newOperator === 'notExists') {
|
||||
if ('value' in newCondition) delete newCondition.value;
|
||||
} else if (!('value' in newCondition)) {
|
||||
(newCondition as BinaryFilterCondition).value = '';
|
||||
}
|
||||
props.onConditionChange({
|
||||
...newCondition,
|
||||
operator: newOperator,
|
||||
} as FilterCondition);
|
||||
}}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFieldText
|
||||
data-test-subj="streamsAppFilterFormFieldText"
|
||||
aria-label={i18n.translate('xpack.streams.filter.field', { defaultMessage: 'Field' })}
|
||||
compressed
|
||||
placeholder={i18n.translate('xpack.streams.filter.fieldPlaceholder', {
|
||||
defaultMessage: 'Field',
|
||||
})}
|
||||
value={props.condition.field}
|
||||
onChange={(e) => {
|
||||
handleConditionChange({ field: e.target.value });
|
||||
}}
|
||||
/>
|
||||
<EuiSelect
|
||||
aria-label={i18n.translate('xpack.streams.filter.operator', {
|
||||
defaultMessage: 'Operator',
|
||||
})}
|
||||
data-test-subj="streamsAppFilterFormSelect"
|
||||
options={operatorOptions}
|
||||
value={props.condition.operator}
|
||||
compressed
|
||||
onChange={handleOperatorChange}
|
||||
/>
|
||||
{'value' in props.condition && (
|
||||
<EuiFlexItem grow>
|
||||
<EuiFieldText
|
||||
aria-label={i18n.translate('xpack.streams.filter.value', { defaultMessage: 'Value' })}
|
||||
placeholder={i18n.translate('xpack.streams.filter.valuePlaceholder', {
|
||||
defaultMessage: 'Value',
|
||||
})}
|
||||
compressed
|
||||
value={String(props.condition.value)}
|
||||
data-test-subj="streamsAppFilterFormValueText"
|
||||
onChange={(e) => {
|
||||
props.onConditionChange({
|
||||
...props.condition,
|
||||
value: e.target.value,
|
||||
} as BinaryFilterCondition);
|
||||
}}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFieldText
|
||||
aria-label={i18n.translate('xpack.streams.filter.value', { defaultMessage: 'Value' })}
|
||||
placeholder={i18n.translate('xpack.streams.filter.valuePlaceholder', {
|
||||
defaultMessage: 'Value',
|
||||
})}
|
||||
compressed
|
||||
value={String(props.condition.value)}
|
||||
data-test-subj="streamsAppFilterFormValueText"
|
||||
onChange={(e) => {
|
||||
handleConditionChange({ value: e.target.value });
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
isBinaryFilterCondition,
|
||||
isFilterCondition,
|
||||
isNeverCondition,
|
||||
isOrCondition,
|
||||
} from '@kbn/streams-schema';
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
@ -52,7 +53,7 @@ export function ConditionMessage({ condition }: { condition: Condition }) {
|
|||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.streams.andDisplay.andLabel"
|
||||
defaultMessage="{left} and {right}"
|
||||
defaultMessage="{left} AND {right}"
|
||||
values={{
|
||||
left: <ConditionMessage condition={condition.and[0]} />,
|
||||
right: (
|
||||
|
@ -66,22 +67,37 @@ export function ConditionMessage({ condition }: { condition: Condition }) {
|
|||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (condition.or.length === 0) {
|
||||
return '';
|
||||
} else if (isOrCondition(condition)) {
|
||||
if (condition.or.length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (condition.or.length === 1) {
|
||||
return <ConditionMessage condition={condition.or[0]} />;
|
||||
}
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.streams.orDisplay.orLabel"
|
||||
defaultMessage="{left} OR {right}"
|
||||
values={{
|
||||
left: <ConditionMessage condition={condition.or[0]} />,
|
||||
right: (
|
||||
<ConditionMessage
|
||||
condition={{
|
||||
...condition,
|
||||
or: condition.or.slice(1),
|
||||
}}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (condition.or.length === 1) {
|
||||
return <ConditionMessage condition={condition.or[0]} />;
|
||||
}
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.streams.orDisplay.orLabel"
|
||||
defaultMessage="{left} or {right}"
|
||||
values={{
|
||||
left: <ConditionMessage condition={condition.or[0]} />,
|
||||
right: <ConditionMessage condition={condition.or[1]} />,
|
||||
}}
|
||||
id="xpack.streams.orDisplay.invalidConditionLabel"
|
||||
defaultMessage="Invalid condition format"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import { css } from '@emotion/css';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import React from 'react';
|
||||
import { EMPTY_EQUALS_CONDITION } from '../../../util/condition';
|
||||
import { ALWAYS_CONDITION } from '../../../util/condition';
|
||||
import { NestedView } from '../../nested_view';
|
||||
import { useRoutingStateContext } from './hooks/routing_state';
|
||||
import { CurrentStreamEntry } from './current_stream_entry';
|
||||
|
@ -82,7 +82,7 @@ export function ChildStreamList({ availableStreams }: { availableStreams: string
|
|||
isNew: true,
|
||||
child: {
|
||||
destination: `${definition.stream.name}.child`,
|
||||
if: cloneDeep(EMPTY_EQUALS_CONDITION),
|
||||
if: cloneDeep(ALWAYS_CONDITION),
|
||||
},
|
||||
});
|
||||
}}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import { EuiFlexGroup, EuiButton, EuiFlexItem, EuiButtonEmpty, EuiToolTip } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { toMountPoint } from '@kbn/react-kibana-mount';
|
||||
import { IngestUpsertRequest } from '@kbn/streams-schema';
|
||||
import { IngestUpsertRequest, isCondition } from '@kbn/streams-schema';
|
||||
import React from 'react';
|
||||
import { useAbortController } from '@kbn/react-hooks';
|
||||
import { useKibana } from '../../../hooks/use_kibana';
|
||||
|
@ -206,7 +206,12 @@ export function ControlBar() {
|
|||
>
|
||||
<EuiButton
|
||||
isLoading={routingAppState.saveInProgress}
|
||||
disabled={routingAppState.saveInProgress || !definition.privileges.manage}
|
||||
disabled={
|
||||
routingAppState.saveInProgress ||
|
||||
!definition.privileges.manage ||
|
||||
(routingAppState.childUnderEdit &&
|
||||
!isCondition(routingAppState.childUnderEdit.child.if))
|
||||
}
|
||||
onClick={saveOrUpdateChildren}
|
||||
data-test-subj="streamsAppStreamDetailRoutingSaveButton"
|
||||
>
|
||||
|
|
|
@ -9,7 +9,7 @@ import { EuiPanel, EuiFlexGroup, EuiFormRow, EuiFieldText } from '@elastic/eui';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { RoutingDefinition } from '@kbn/streams-schema';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { ConditionEditor } from '../condition_editor';
|
||||
import { RoutingConditionEditor } from '../condition_editor';
|
||||
import { ControlBar } from './control_bar';
|
||||
|
||||
export function NewRoutingStreamEntry({
|
||||
|
@ -51,8 +51,7 @@ export function NewRoutingStreamEntry({
|
|||
}}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<ConditionEditor
|
||||
isNew
|
||||
<RoutingConditionEditor
|
||||
condition={child.if}
|
||||
onConditionChange={(condition) => {
|
||||
onChildChange({
|
||||
|
|
|
@ -22,8 +22,7 @@ import { RoutingDefinition, isDescendantOf, isNeverCondition } from '@kbn/stream
|
|||
import React from 'react';
|
||||
import { css } from '@emotion/css';
|
||||
import { useStreamsAppRouter } from '../../../hooks/use_streams_app_router';
|
||||
import { alwaysToEmptyEquals } from '../../../util/condition';
|
||||
import { ConditionEditor } from '../condition_editor';
|
||||
import { RoutingConditionEditor } from '../condition_editor';
|
||||
import { ConditionMessage } from '../condition_message';
|
||||
import { ControlBar } from './control_bar';
|
||||
|
||||
|
@ -140,8 +139,8 @@ export function RoutingStreamEntry({
|
|||
</EuiFlexGroup>
|
||||
{edit && (
|
||||
<EuiFlexGroup direction="column" gutterSize="s">
|
||||
<ConditionEditor
|
||||
condition={alwaysToEmptyEquals(child.if)}
|
||||
<RoutingConditionEditor
|
||||
condition={child.if}
|
||||
onConditionChange={(condition) => {
|
||||
onChildChange({
|
||||
...child,
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
WiredStreamGetResponse,
|
||||
conditionToQueryDsl,
|
||||
getFields,
|
||||
isAlwaysCondition,
|
||||
} from '@kbn/streams-schema';
|
||||
import useToggle from 'react-use/lib/useToggle';
|
||||
import { MappingRuntimeField, MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types';
|
||||
|
@ -51,7 +52,7 @@ export const useAsyncSample = (options: Options) => {
|
|||
|
||||
const convertedCondition = useMemo(() => {
|
||||
const condition = options.condition ? emptyEqualsToAlways(options.condition) : undefined;
|
||||
return condition && 'always' in condition ? undefined : condition;
|
||||
return condition && isAlwaysCondition(condition) ? undefined : condition;
|
||||
}, [options.condition]);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
type AlwaysCondition,
|
||||
type BinaryFilterCondition,
|
||||
type Condition,
|
||||
NeverCondition,
|
||||
} from '@kbn/streams-schema';
|
||||
import { cloneDeep, isEqual } from 'lodash';
|
||||
|
||||
|
@ -21,6 +22,8 @@ export const EMPTY_EQUALS_CONDITION: BinaryFilterCondition = Object.freeze({
|
|||
|
||||
export const ALWAYS_CONDITION: AlwaysCondition = Object.freeze({ always: {} });
|
||||
|
||||
export const NEVER_CONDITION: NeverCondition = Object.freeze({ never: {} });
|
||||
|
||||
export function alwaysToEmptyEquals<T extends Condition>(condition: T): Exclude<T, AlwaysCondition>;
|
||||
|
||||
export function alwaysToEmptyEquals(condition: Condition) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue