diff --git a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/shared/disabled_badge.tsx b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/shared/disabled_badge.tsx
new file mode 100644
index 0000000000000..5de8c84a9bace
--- /dev/null
+++ b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/shared/disabled_badge.tsx
@@ -0,0 +1,20 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React from 'react';
+import { EuiBadge } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+
+export function DisabledBadge() {
+ return (
+
+ {i18n.translate('xpack.streams.streamDetailRouting.disabled', {
+ defaultMessage: 'Disabled',
+ })}
+
+ );
+}
diff --git a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/shared/index.ts b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/shared/index.ts
index 1be81cfe4fd32..bf90f02f24482 100644
--- a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/shared/index.ts
+++ b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/shared/index.ts
@@ -8,3 +8,5 @@ export { PreviewFlyout } from './preview_flyout';
export type { DataTableRecordWithIndex } from './preview_flyout';
export { MemoPreviewTable } from './preview_table';
export { ConditionPanel, ConditionDisplay, EditableConditionPanel } from './condition_display';
+export { VerticalRule } from './vertical_rule';
+export { DisabledBadge } from './disabled_badge';
diff --git a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/shared/vertical_rule.tsx b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/shared/vertical_rule.tsx
new file mode 100644
index 0000000000000..0680d1d237ea6
--- /dev/null
+++ b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/shared/vertical_rule.tsx
@@ -0,0 +1,31 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React from 'react';
+import styled from '@emotion/styled';
+import { useEuiTheme } from '@elastic/eui';
+
+export function VerticalRule() {
+ const { euiTheme } = useEuiTheme();
+
+ const CentralizedContainer = styled.div`
+ display: flex;
+ align-items: center;
+ padding: 0 ${euiTheme.size.xs};
+ `;
+
+ const Border = styled.div`
+ height: 20px;
+ border-right: ${euiTheme.border.thin};
+ `;
+
+ return (
+
+
+
+ );
+}
diff --git a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/child_stream_list.tsx b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/child_stream_list.tsx
index dd7fd52d6ae09..45bb60a644a2b 100644
--- a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/child_stream_list.tsx
+++ b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/child_stream_list.tsx
@@ -68,6 +68,7 @@ export function ChildStreamList({ availableStreams }: { availableStreams: string
previewSuggestion,
acceptSuggestion,
rejectSuggestion,
+ updateSuggestion,
} = useReviewSuggestionsForm();
const { currentRuleId, definition, routing } = routingSnapshot.context;
@@ -233,6 +234,7 @@ export function ChildStreamList({ availableStreams }: { availableStreams: string
rejectSuggestion={rejectSuggestion}
resetForm={resetForm}
suggestions={suggestions}
+ updateSuggestion={updateSuggestion}
/>
)
) : null}
diff --git a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/control_bars.tsx b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/control_bars.tsx
index 0f8efe743cde0..75e454a6f6cd3 100644
--- a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/control_bars.tsx
+++ b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/control_bars.tsx
@@ -79,6 +79,34 @@ export const EditRoutingRuleControls = ({
);
};
+export const EditSuggestedRuleControls = ({ onSave }: { onSave?: () => void }) => {
+ const routingSnapshot = useStreamsRoutingSelector((snapshot) => snapshot);
+ const { cancelChanges, saveEditedSuggestion } = useStreamRoutingEvents();
+
+ const canSave = routingSnapshot.can({ type: 'suggestion.saveSuggestion' });
+ const hasPrivileges = routingSnapshot.context.definition.privileges.manage;
+
+ const handleUpdate = () => {
+ if (onSave) {
+ onSave();
+ }
+ saveEditedSuggestion();
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
const RemoveButton = ({
isDisabled,
onDelete,
@@ -125,8 +153,8 @@ const SaveButton = (props: EuiButtonPropsForButton) => (
const UpdateButton = (props: EuiButtonPropsForButton) => (
- {i18n.translate('xpack.streams.streamDetailRouting.change', {
- defaultMessage: 'Change routing',
+ {i18n.translate('xpack.streams.streamDetailRouting.update', {
+ defaultMessage: 'Update',
})}
);
diff --git a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/idle_routing_stream_entry.tsx b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/idle_routing_stream_entry.tsx
index 8a5d1811071d3..5602dc53a85ba 100644
--- a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/idle_routing_stream_entry.tsx
+++ b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/idle_routing_stream_entry.tsx
@@ -24,30 +24,10 @@ import { i18n } from '@kbn/i18n';
import { isDescendantOf, isRoutingEnabled } from '@kbn/streams-schema';
import { css } from '@emotion/css';
import { css as cssReact } from '@emotion/react';
-import styled from '@emotion/styled';
import { useStreamsAppRouter } from '../../../hooks/use_streams_app_router';
-import { ConditionPanel } from '../shared';
+import { ConditionPanel, VerticalRule } from '../shared';
import type { RoutingDefinitionWithUIAttributes } from './types';
-
-function VerticalRule() {
- const { euiTheme } = useEuiTheme();
- const CentralizedContainer = styled.div`
- display: flex;
- align-items: center;
- padding: 0 ${euiTheme.size.xs};
- `;
-
- const Border = styled.div`
- height: 20px;
- border-right: ${euiTheme.border.thin};
- `;
-
- return (
-
-
-
- );
-}
+import { DisabledBadge } from '../shared';
export function IdleRoutingStreamEntry({
availableStreams,
@@ -143,11 +123,7 @@ export function IdleRoutingStreamEntry({
>
{!isRoutingEnabled(routingRule.status) && (
<>
-
- {i18n.translate('xpack.streams.streamDetailRouting.disabled', {
- defaultMessage: 'Disabled',
- })}
-
+
>
)}
diff --git a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/index.tsx b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/index.tsx
index 5ba3aa97655f8..03e83b1c2de1b 100644
--- a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/index.tsx
+++ b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/index.tsx
@@ -11,7 +11,6 @@ import {
EuiFlexItem,
EuiPanel,
EuiResizableContainer,
- useIsWithinBreakpoints,
} from '@elastic/eui';
import { css } from '@emotion/css';
import type { Streams } from '@kbn/streams-schema';
@@ -107,7 +106,6 @@ export function StreamDetailRoutingImpl() {
});
const availableStreams = streamsListFetch.value?.streams.map((stream) => stream.name) ?? [];
- const isVerticalLayout = useIsWithinBreakpoints(['xs', 's']);
return (
-
+
{(EuiResizablePanel, EuiResizableButton) => (
<>
;
} else if (
routingSnapshot.matches({ ready: 'creatingNewRule' }) ||
- routingSnapshot.matches({ ready: 'reviewSuggestedRule' })
+ routingSnapshot.matches({ ready: 'reviewSuggestedRule' }) ||
+ routingSnapshot.matches({ ready: 'editingSuggestedRule' })
) {
content = ;
}
diff --git a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/review_suggestions_form/create_stream_confirmation_modal.tsx b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/review_suggestions_form/create_stream_confirmation_modal.tsx
index 161e45b900638..9b2f8a465b23d 100644
--- a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/review_suggestions_form/create_stream_confirmation_modal.tsx
+++ b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/review_suggestions_form/create_stream_confirmation_modal.tsx
@@ -84,7 +84,11 @@ export function CreateStreamConfirmationModal({
forkStream({
destination: partition.name,
where: partition.condition,
- }).then(() => onSuccess())
+ }).then((result) => {
+ if (result.success) {
+ onSuccess();
+ }
+ })
}
fill
>
diff --git a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/review_suggestions_form/review_suggestions_form.tsx b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/review_suggestions_form/review_suggestions_form.tsx
index 9f429505a9476..b3f5b97ab2d3d 100644
--- a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/review_suggestions_form/review_suggestions_form.tsx
+++ b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/review_suggestions_form/review_suggestions_form.tsx
@@ -20,6 +20,7 @@ import type {
import {
useStreamSamplesSelector,
useStreamsRoutingSelector,
+ useStreamRoutingEvents,
} from '../state_management/stream_routing_state_machine';
import { CreateStreamConfirmationModal } from './create_stream_confirmation_modal';
import type { AIFeatures } from '../../../../hooks/use_ai_features';
@@ -32,6 +33,7 @@ export interface ReviewSuggestionsFormProps
| 'previewSuggestion'
| 'acceptSuggestion'
| 'rejectSuggestion'
+ | 'updateSuggestion'
> {
suggestions: PartitionSuggestion[];
onRegenerate: (connectorId: string) => void;
@@ -48,11 +50,20 @@ export function ReviewSuggestionsForm({
previewSuggestion,
acceptSuggestion,
rejectSuggestion,
+ updateSuggestion,
onRegenerate,
}: ReviewSuggestionsFormProps) {
const ruleUnderReview = useStreamsRoutingSelector((snapshot) =>
snapshot.matches({ ready: 'reviewSuggestedRule' }) ? snapshot.context.suggestedRuleId : null
);
+ const editingSuggestion = useStreamsRoutingSelector((snapshot) =>
+ snapshot.matches({ ready: 'editingSuggestedRule' }) ? snapshot.context.editedSuggestion : null
+ );
+
+ // For the confirmation modal, use edited suggestion if available, otherwise find by name
+ const partitionForModal =
+ editingSuggestion || suggestions.find(({ name }) => name === ruleUnderReview)!;
+
const selectedPreviewName = useStreamSamplesSelector(
({ context }) =>
context.selectedPreview &&
@@ -60,17 +71,34 @@ export function ReviewSuggestionsForm({
context.selectedPreview.name
);
+ const { editSuggestion } = useStreamRoutingEvents();
+ const routingSnapshot = useStreamsRoutingSelector((snapshot) => snapshot);
+
+ const handleSave = () => {
+ const currentEditingIndex = routingSnapshot.context.editingSuggestionIndex;
+ const currentEditedSuggestion = routingSnapshot.context.editedSuggestion;
+
+ if (currentEditingIndex !== null && currentEditedSuggestion) {
+ updateSuggestion(currentEditingIndex, currentEditedSuggestion);
+ }
+ };
+
return (
<>
- {ruleUnderReview && (
+ {ruleUnderReview && partitionForModal && (
name === ruleUnderReview)!}
- onSuccess={() =>
- acceptSuggestion(suggestions.findIndex(({ name }) => name === ruleUnderReview)!)
- }
+ partition={partitionForModal}
+ onSuccess={() => {
+ acceptSuggestion(
+ editingSuggestion
+ ? routingSnapshot.context.editingSuggestionIndex!
+ : suggestions.findIndex(({ name }) => name === ruleUnderReview)!
+ );
+ }}
/>
)}
previewSuggestion(index, toggle)}
onDismiss={() => rejectSuggestion(index, selectedPreviewName === partition.name)}
+ onEdit={editSuggestion}
+ onSave={handleSave}
/>
diff --git a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/review_suggestions_form/suggested_stream_panel.tsx b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/review_suggestions_form/suggested_stream_panel.tsx
index f091118ebf616..e379492408df4 100644
--- a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/review_suggestions_form/suggested_stream_panel.tsx
+++ b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/review_suggestions_form/suggested_stream_panel.tsx
@@ -15,45 +15,189 @@ import {
EuiText,
EuiIcon,
EuiLoadingSpinner,
+ EuiButtonIcon,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';
-import type { Streams } from '@kbn/streams-schema';
+import { type Streams } from '@kbn/streams-schema';
+import { isCondition } from '@kbn/streamlang';
import type { PartitionSuggestion } from './use_review_suggestions_form';
import { useMatchRate } from './use_match_rate';
import {
useStreamRoutingEvents,
+ useStreamsRoutingSelector,
useStreamSamplesSelector,
} from '../state_management/stream_routing_state_machine/use_stream_routing';
import { SelectablePanel } from './selectable_panel';
-import { ConditionPanel } from '../../shared';
+import { ConditionPanel, VerticalRule } from '../../shared';
+import { StreamNameFormRow } from '../stream_name_form_row';
+import { RoutingConditionEditor } from '../routing_condition_editor';
+import { processCondition } from '../utils';
export function SuggestedStreamPanel({
definition,
partition,
onDismiss,
onPreview,
+ index,
+ onEdit,
+ onSave,
}: {
definition: Streams.WiredStream.GetResponse;
partition: PartitionSuggestion;
onDismiss(): void;
onPreview(toggle: boolean): void;
+ index: number;
+ onEdit(index: number, suggestion: PartitionSuggestion): void;
+ onSave?: () => void;
}) {
- const matchRate = useMatchRate(definition, partition);
+ const routingSnapshot = useStreamsRoutingSelector((snapshot) => snapshot);
+ const {
+ changeSuggestionName,
+ changeSuggestionCondition,
+ reviewSuggestedRule,
+ cancelChanges,
+ saveEditedSuggestion,
+ } = useStreamRoutingEvents();
+
+ const [nameError, setNameError] = React.useState(undefined);
+ const [conditionError, setConditionError] = React.useState(undefined);
+
+ const editedSuggestion = routingSnapshot.context.editedSuggestion;
+ const isEditing =
+ routingSnapshot.matches({ ready: 'editingSuggestedRule' }) &&
+ routingSnapshot.context.editingSuggestionIndex === index;
+
+ // Use edited suggestion when editing, otherwise use original partition
+ const currentSuggestion = isEditing && editedSuggestion ? editedSuggestion : partition;
+ const matchRate = useMatchRate(definition, currentSuggestion);
+
const selectedPreview = useStreamSamplesSelector((snapshot) => snapshot.context.selectedPreview);
const isSelected = Boolean(
selectedPreview &&
selectedPreview.type === 'suggestion' &&
- selectedPreview.name === partition.name
+ selectedPreview.name === currentSuggestion.name
);
- const { reviewSuggestedRule } = useStreamRoutingEvents();
+
+ const handleNameChange = (name: string) => {
+ if (!isEditing) return;
+ const isDuplicateName = routingSnapshot.context.routing.some((r) => r.destination === name);
+
+ if (isDuplicateName) {
+ setNameError(
+ i18n.translate('xpack.streams.streamDetailRouting.nameConflictError', {
+ defaultMessage: 'A stream with this name already exists',
+ })
+ );
+ } else {
+ setNameError(undefined);
+ }
+
+ changeSuggestionName(name);
+ };
+
+ const handleConditionChange = (condition: any) => {
+ if (!isEditing) return;
+ const processedCondition = processCondition(condition);
+ const isProcessedCondition = processedCondition ? isCondition(processedCondition) : true;
+
+ if (!isProcessedCondition) {
+ setConditionError(
+ i18n.translate('xpack.streams.streamDetailRouting.conditionRequiredError', {
+ defaultMessage: 'Condition is required',
+ })
+ );
+ } else {
+ setConditionError(undefined);
+ }
+
+ changeSuggestionCondition(condition);
+ };
+
+ if (isEditing) {
+ return (
+
+
+
+ {}}
+ isSuggestionRouting={true}
+ />
+
+
+
+ {i18n.translate('xpack.streams.streamDetailRouting.cancel', {
+ defaultMessage: 'Cancel',
+ })}
+
+
+
+
+
+ {
+ if (onSave) {
+ onSave();
+ }
+ saveEditedSuggestion();
+ }}
+ >
+ {i18n.translate('xpack.streams.streamDetailRouting.update', {
+ defaultMessage: 'Update',
+ })}
+
+
+
+ {
+ if (isEditing && onSave) {
+ onSave();
+ }
+ reviewSuggestedRule(currentSuggestion.name || partition.name);
+ }}
+ fill
+ >
+ {i18n.translate(
+ 'xpack.streams.streamDetailRouting.suggestedStreamPanel.accept',
+ {
+ defaultMessage: 'Update & Accept',
+ }
+ )}
+
+
+
+
+
+
+
+ );
+ }
return (
-
+
- {partition.name}
+ {currentSuggestion.name}
{matchRate.loading ? (
@@ -62,19 +206,26 @@ export function SuggestedStreamPanel({
) : matchRate.value !== undefined ? (
<>
-
-
-
-
-
- {matchRate.value}
-
-
+
+
+ {matchRate.value}
+
>
) : null}
+
+
+
+ onEdit(index, currentSuggestion)}
+ aria-label={i18n.translate('xpack.streams.streamDetailRouting.edit', {
+ defaultMessage: 'Edit',
+ })}
+ data-test-subj={`suggestionEditButton-${currentSuggestion.name}`}
+ />
-
+
@@ -83,6 +234,12 @@ export function SuggestedStreamPanel({
isSelected={isSelected}
size="s"
onClick={() => onPreview(!isSelected)}
+ aria-label={i18n.translate(
+ 'xpack.streams.streamDetailRouting.suggestedStreamPanel.preview',
+ {
+ defaultMessage: 'Preview',
+ }
+ )}
>
{i18n.translate('xpack.streams.streamDetailRouting.suggestedStreamPanel.preview', {
defaultMessage: 'Preview',
@@ -92,7 +249,16 @@ export function SuggestedStreamPanel({
-
+
{i18n.translate('xpack.streams.streamDetailRouting.suggestedStreamPanel.dismiss', {
defaultMessage: 'Reject',
})}
@@ -102,7 +268,7 @@ export function SuggestedStreamPanel({
reviewSuggestedRule(partition.name)}
+ onClick={() => reviewSuggestedRule(currentSuggestion.name || partition.name)}
fill
>
{i18n.translate('xpack.streams.streamDetailRouting.suggestedStreamPanel.accept', {
diff --git a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/review_suggestions_form/use_review_suggestions_form.tsx b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/review_suggestions_form/use_review_suggestions_form.tsx
index 2f5856df4c67a..fec528063e58b 100644
--- a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/review_suggestions_form/use_review_suggestions_form.tsx
+++ b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/review_suggestions_form/use_review_suggestions_form.tsx
@@ -90,6 +90,13 @@ export function useReviewSuggestionsForm() {
}
};
+ const updateSuggestion = (index: number, updates: Partial) => {
+ if (!suggestions) return;
+ const updatedSuggestion = { ...suggestions[index], ...updates };
+ const updatedSuggestions = suggestions.toSpliced(index, 1, updatedSuggestion);
+ setSuggestions(updatedSuggestions);
+ };
+
const resetPreview = () => {
streamsRoutingActorRef.send({
type: 'suggestion.preview',
@@ -118,6 +125,7 @@ export function useReviewSuggestionsForm() {
isLoadingSuggestions,
fetchSuggestions,
resetForm,
+ updateSuggestion,
previewSuggestion: (index: number, toggle?: boolean) => {
if (suggestions) {
const partition = suggestions[index];
diff --git a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/routing_condition_editor.tsx b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/routing_condition_editor.tsx
index fca3d3652bb25..ef40a16c74a1f 100644
--- a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/routing_condition_editor.tsx
+++ b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/routing_condition_editor.tsx
@@ -18,38 +18,44 @@ type RoutingConditionChangeParams = Omit;
export type RoutingConditionEditorProps = Omit & {
onStatusChange: (params: RoutingConditionChangeParams['status']) => void;
+ isSuggestionRouting?: boolean;
};
export function RoutingConditionEditor(props: RoutingConditionEditorProps) {
- const isEnabled = isRoutingEnabled(props.status);
+ const { isSuggestionRouting, status } = props;
+ const isEnabled = isRoutingEnabled(status);
const fieldSuggestions = useRoutingFieldSuggestions();
return (
-
- {i18n.translate('xpack.streams.routing.conditionEditor.title', {
- defaultMessage: 'Status',
- })}
-
+ {i18n.translate('xpack.streams.routing.conditionEditor.title', {
+ defaultMessage: 'Status',
})}
- />
-
- }
- >
- props.onStatusChange(event.target.checked ? 'enabled' : 'disabled')}
- />
-
+
+
+ }
+ >
+
+ props.onStatusChange(event.target.checked ? 'enabled' : 'disabled')
+ }
+ />
+
+ )}
);
diff --git a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/state_management/stream_routing_state_machine/routing_samples_state_machine.ts b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/state_management/stream_routing_state_machine/routing_samples_state_machine.ts
index 9d181c6f65e4c..a883b05503be0 100644
--- a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/state_management/stream_routing_state_machine/routing_samples_state_machine.ts
+++ b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/state_management/stream_routing_state_machine/routing_samples_state_machine.ts
@@ -57,6 +57,10 @@ export type RoutingSamplesEvent =
| {
type: 'routingSamples.setSelectedPreview';
preview: RoutingSamplesContext['selectedPreview'];
+ }
+ | {
+ type: 'routingSamples.updatePreviewName';
+ name: string;
};
export interface SearchParams extends RoutingSamplesInput {
@@ -109,6 +113,17 @@ export const routingSamplesMachine = setup({
selectedPreview: params.preview,
})
),
+ updatePreviewName: assign(({ context }, params: { name: string }) => {
+ if (!context.selectedPreview || context.selectedPreview.type !== 'suggestion') {
+ return {};
+ }
+ return {
+ selectedPreview: {
+ ...context.selectedPreview,
+ name: params.name,
+ },
+ };
+ }),
},
delays: {
conditionUpdateDebounceTime: 500,
@@ -165,6 +180,14 @@ export const routingSamplesMachine = setup({
},
],
},
+ 'routingSamples.updatePreviewName': {
+ actions: [
+ {
+ type: 'updatePreviewName',
+ params: ({ event }) => event,
+ },
+ ],
+ },
},
states: {
debouncingCondition: {
diff --git a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/state_management/stream_routing_state_machine/stream_routing_state_machine.ts b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/state_management/stream_routing_state_machine/stream_routing_state_machine.ts
index 877da5376e710..15ce8fe2d983e 100644
--- a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/state_management/stream_routing_state_machine/stream_routing_state_machine.ts
+++ b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/state_management/stream_routing_state_machine/stream_routing_state_machine.ts
@@ -31,6 +31,7 @@ import {
createRoutingSamplesMachineImplementations,
routingSamplesMachine,
} from './routing_samples_state_machine';
+import type { PartitionSuggestion } from '../../review_suggestions_form/use_review_suggestions_form';
export type StreamRoutingActorRef = ActorRefFrom;
@@ -105,6 +106,23 @@ export const streamRoutingMachine = setup({
resetSuggestedRuleId: assign(() => ({
suggestedRuleId: null,
})),
+ storeEditingSuggestion: assign(
+ (_, params: { index: number; suggestion: PartitionSuggestion }) => ({
+ editingSuggestionIndex: params.index,
+ editedSuggestion: params.suggestion,
+ })
+ ),
+ updateEditedSuggestion: assign(
+ ({ context }, params: { updates: Partial }) => ({
+ editedSuggestion: context.editedSuggestion
+ ? { ...context.editedSuggestion, ...params.updates }
+ : null,
+ })
+ ),
+ clearEditingSuggestion: assign(() => ({
+ editingSuggestionIndex: null,
+ editedSuggestion: null,
+ })),
},
guards: {
canForkStream: and(['hasManagePrivileges', 'isValidRouting']),
@@ -118,7 +136,7 @@ export const streamRoutingMachine = setup({
isSchema(routingDefinitionListSchema, context.routing.map(routingConverter.toAPIDefinition)),
},
}).createMachine({
- /** @xstate-layout N4IgpgJg5mDOIC5QCcD2BXALgSwHZQGVNkwBDAWwDo9sdSAbbALzygGIBtABgF1FQADqli1sqXPxAAPRACZZATkoA2AIwAWABwL1sgKybZG1bIA0IAJ6IAzKr2V1XLgs3WA7Jq7uFb5QF8-czQsViISCkpwiAs2WGIyKhIAYzBsADdIbj4kECERHHFJGQRVBS5ZSh9y8uU9PWtrPTdzKwRZdWVKTWVZQz0FOx7ZNz0AoIwcfDCEyLJo6gh6MDZgyagAJXQlyiTwzDAsyTzRQpzi9V1KPWUFAe1Pa2UevRbEVVLOrmv39W9DdTGIFWoXiESiFgWSxWE1Ym22kFohxyxwKEjONk09lkQ00vgMei4yk0rxKhK4lDcdzc1gGqlqdkBwKmoMScwh2EWyyZGy2YFmqGQEDAyCRgmEJzRoGKAFpVNYuuo3HYdKpCQplE5iZY3rIuOpKIpugomnpdN0AYEgTDmeFWaR5rsyGsAHJgADucL5SQAFqR8KxoSF8J6dn6UvRRblxaiim8vJouho9HLNJjk8p1CSbhUCXTGj51WprIzrYQWbN7RDHaQXe6Qz6-VAA9z6778AdeEdo2JJdI3o8lNZ1Ap3Lj2t0ta0iRVbCPDO9rJ4TCWg2XbRWHXtWK6Pbydm2m-hA2sQwjMJGUT3YyUMzm9L1hh03PpVCTKaoHNcDI0NNiRyu1mmME2R2Ld8B3VtG2bUsQwAMwFABrC9u1OKU41+FRHBpW5qWUIdM21Uk9QpDV500Ewbn-S1uSAu1Nydbc6z3eDkAQgMIHEPk8DSVAEL5FiENogBBJJMAFZD8ivdEEGpD8enKNxFMcZ8BjfYdKBpJpFEJG4mlUACQXXcFQIY8CmO2ASA2FNBkEoAR6BrFiqAE4TRPEztkRQ3tilVHwHGpLRVBGWx3jMQjtHsJofFqU03EVTQLXGVdaI3CEz1hPcG39I8W0yvYO2yMVJNQvsSkaTolQ8Wd3gSkcSVC-UPF1OpFRcPVi2o0sUuM9Lg0yg9oNXeswzACMPKKiVr0MeVXD1dV3D0lx6tNckOgUXUjC+dp5AMm0Zh6iBRD67YssPdhcpOg8Cq7YrvLeNxnEoPUCWxZQ4sVRd6rehMLmfDQuA8e9Rk65LywOo6eUuqCcpgvczwkybpLsIwHFUWbzX0Wd6pChwCwUh7XCaXa132kDeshr0Bphoa4cO89VEKqNbuvIKOieoldB6X5+n0erfiUfouA0dx3jnNxie6sm6YyqHsvO2HthIchUAyBGYyRoLOkcbp3Cedb1HqPn2hUP8Ae2g2EolsGpYhyC5ePGW+VgUhVfGpnEbQkoPgNXodF6AxdEeerfHJVVuiFgHbhMcWQcA63K0ocmQyVlXHbYDjcC43AeL4yghSWfZXLEkU3cvErikaNwunI-n4rUIKXkIkwLie75hcpf4raMm2Tz3FO0jT6yBTshzMCcvPRrAQuWRE4u1akz2TEUfy6gDnRasnN56i12bCXkWRGkMLvSYTpO93QAQIBrNOM6znO+Qv2BhUwIv3MZsu7oQSuDSCw+LnKRUFxg76EoHYU0Pg6RaHXsfYCp9pbHQfpfa+CC2BD1svZRyAoqCP2fq-Eu78vIs3kAmciGhfB-SGF9IcVxGh6QzN8PUMC6IQhIAKIUyBHawH5IKYUg1e6KzAGw4U89y79h6D7awAMCRaCHAoEkzVyRxV-v0OUdJ9Kx0MifeYrCeEcIQVwnR7C+GO1DLgcMIjP4Eh+joY0NIYqmkbq0XUQUnoAIaLoAGHQmGpW4UY-RvjeHU34U7F211PLMyRl8ckTxhw6BuAlRQyh5ENCrpRcoi40Z4UYRovasDtGCN0ZwygF8r5x1tOnTi1Bs68UQU-ZAL8Z5uXwTdD2pVA5XHXs+A+9Rky-HkUYTopR1ClEeIqL43jjKGMCRTLhJTkEkwoKg5ANkR6YOQNggQdSGm2lnm-Fp6tF6qnJLqDwHclRAMIvIR6AwMkJSVO4AIlpcCoCFPAHINEWT7IXqVWUj07nKmGWqDUr4m4t3oWUDwPhXDWHaN4mgdBGAsHwF80RbQ6RdCeEYIKQVPDDmsMkioSYyg3LRnmYGSUylaNaBNA5Py1AKiVP0QFNxgUkkXFXWcjgfAjHvOMnJCzmGQjACiz++glBqjuG1R4zwSQJQTD0P6DR1TkRjhSzReSqxgSgBBXkIrrxKnJF8LQwx9CeF6CSe8H5yIjluPoYZosJkgWrLWXcsszp6qRv7BwQxKTbVxORWVGgHANB+KmLa6i1W5MFc6xirr+KIVYB6z2IwExGsMM+Aw5RN4IG0PKOoeELiY2-AoR1cDba6vCa04oNwq4G3Ws+fM1I5FN01v5bmdqPH+H5ZLMtwT9zQygEm0qdJ5AaV9QYXwBtxzLRHBpGxdR1QE1VVaUG3de0mP7o7IdxQRgVCqvXOVcU9TY1VJUd6e94wDFLfMM+2w5nBO3YgOK9g60msbTSYOjwVBcExPofQ1JTSaGvSwgpfiZmPoQLpCkcVfC-qFsMfFTd5oGmGcaHl7hhyyGAwEvRMycOJsrbSnyki80UUUC4GkQ4kmXLId6-ouIyjOEkeSldlKNX4f8fe9V5AINxXlEpWDvLo6IacScroZR3AwphZSU0jy-BAA */
+ /** @xstate-layout N4IgpgJg5mDOIC5QCcD2BXALgSwHZQGVNkwBDAWwDo9sdSAbbALzygGIBtABgF1FQADqli1sqXPxAAPRAFYATABoQAT0TyuANk2UuATlld9hzQGZNAdgC+V5WiysiJCpWcQVbWMTJUSAYzBsADdIbj4kECERHHFJGQQARllzSj0EgA5tZPSuC1N0i2U1BHMLABZKWU10hU0y-IT5cxs7DBx8Jx9XMnc2e3bCCgF6OEpYMEwAEVQ-dHIwXEwAWVJMPwALADFsekwwZDDJKNFYiPiEsrLZVLqasvT8h9MlVURTC009SnT6pvT-ixpBItED9RzeFxuDywdBQGBeMS4SgCEhBbBgADuhwixxiEjOiASeh0ZVyslkaT0XCeZUKrwQGnkFkoFnJ8k0sgsCS48gMmhBYI6EN8PQ8gqgACV0CNumjMQRYfC9hBsYJhCd8aBzsTKKTWRSidTTKZaUV1Jp2al6oDTFx8rl0gK2uDnCLSL0YXC4HjKJBaKrIuq8XFCTq9eTKUaTXTis8tJRPgorgZzPUnQ4ha7uu6VNQICM+s78FKZX5nHsA7jESHEpYdBYtEDOfIKWbEtyvrSLLzZJcElyyumBp1IaK8wXxSWwL6IP7eEcg9WCQz4+GDVSaTG5FwEqlZOkNJ9SeyLUOXV0oeOwIWM5LpdOSKhkBB9pXF6ctYgytUE5od1d5DKBJ+1kNteX+XQyl5fsCnkJIz0zC8xzLMgBgAOUxKdKA2Uh8FYG8BiwvxcICeg32iJdPwZTldz-eRMhqdkDVA+kLm-XRynZKkG0+TRgVsUEi0IYVs3cbDy1YDCMSI9ZcKgfDJ3vbDZPwMByI1Gt+2AllG2A5tW1Y2pUhqBJTGST5+z0awBPFEc3TElDVkkzClJwvD8AI1gsL9TB1ODZdD11Ml1yjU1WP+a4+IsB5qT0NJTxsoS7NE3NHPQlzSxU+SPMUmUADMnwAaz8yjpFDDRUmeJlzBqUwklMNsEiPFk9EAuq4IeUz+US29ksvNLnOkpSCuQQr8IgcRpzwIJUEK6cRsKuyAEE-EwJ8So-MrqK5BMeQY2pmMavj0iC5I0kBXJ2UdHrhxE-qJPwKSsIW-D9jQZBkXoVYRqoBbltW9b5xxd9NS2jQLl0LhZCSVrWQKBrWLoyoMis-JSSg7rWl6u6xx8rzXKyhShKI8s1KBtUKM284qsoEDyjq+p93ZRqyji2mtGAg8ufkFsEOErNLzx4sCbkonbyIkiwDI8nA0p0HqZ52nWXpi4zIPTQ2zqa5jDqECLiMeQ+b63HZ0IkX3PYXLpzcmANvl9QakoE0zHSNImTgrlGvyUwnaaxouueKp+Kx26BZN0Rhcy0WcuJpSfLtzTjXkJXOXqVWmY1+kPh0PQaihxoN0xwTsbDnMZwju8o4tzzI+neOEnCCmNICoCgv1SNN0a+iTq4ICW37Ulc9do2cbLoXK+twmY-FpSSHIVAQgT5dORO6He-Alte70BHikaJkWRNd51YKP9TBH0uxPHmTo8t2OZVgUhF5lqsqbkaLkfX3PN9ZnfCXML4nj5AMK7d4tJz5ITHqbfGMo54L2gdeCauApq4BmnNSgL4Rh7H+mtA4z8QaJyginFWjN1ZtkyMnFscUNBpHaqycBo5IEVywrAtEtc2BvSfJ9b6T4qAYImGAbBgNG6y2blRLkq8oIGGJByA8ZRGoZC4KkfQrVobmChqzeh9lcxXyUugAQEAnJsMQcg1B049HjGQJgQRuDhEv3tokMyyc6ZpxIczekBgKjJCqsBDIyReyaJSuXM2Mo9EGOCdeDhH1hjcOQFQcx+wrHChWjgpeYj+yUDgrSbecVWochYrvJkzIqhGBAQ8Yk1kQ7ngYWJR8z59jwNgN0J8L5kBi3CU0upNiFxyxrIYH22h-xJmAqyMCxodBMjrBoKG5hyQBMvLUlpDSOmLOnu04iuBSKpK2vrHQvZ6i0h5tSS4egwLu10HBVGFoKRzLHAs+ptdGl3Naas+BYxH5k1sfg5cDZk7Q32eULx28wKGGZL8BsmQoJ9huWXJ5SzQmGP5j4NgxjqAoNmmYgQFjEmumSUI7pojtmkl2ZcaMhyfiszAkBZkeRMhmX-FZPQ0KalgGafciejT4WhyRZErhmAfqUHiZY6xWz4g5F3PuY0udcgciZCc+kgEki0wpKSH47w7TNBulUrRsp0QYgVF6LwkBmFgDlBiNpryFoisQGYH2toNAWEBFcbk3YwKZEUWZLJTM4rklkEy3MqJdX6qVEa2eJrdXmtrthSW0tPk9OXI0B4qRuyWCZLS0h8qLQVD0NaBQ9FiT7j9Tq+UipvQhvykVcak1UWmMoH9JJAMunAzjVRBNPsrLsm7NFF2bjiiFMUQMoC0r0imUHJqxC1T-VhuLQa5Uz0K0eR5dEvlPDa1FWFXg5tW1k1O3KLnaoGhaR1DAh8Huw6MhciMICUyhbx5BtLRAbyUCPKeiVIiZSckwBoQoB8-F-kqJARJEyO0PNLhVFZnI+kh8dD-A6lBQwBoz5jsRROoJjgS2GofXHJ97AX3ejfTbMAABhcQUDxBWoQMeWmacahmHJA6iDsYTS0VyLSBR1Je43uw3ejDj6K41wnu+1S5H6g6CJAaV2w68gGVjIBHQSc6IwXMr6pDxtGHDnQ7OrDfGrZRo2VLcjDYKi0lMK1UyO53gtjbMaK4lQ-wWlZIBYdDxOMV245pmU49PAabfQ-EIbnEQGd7rqPIpnbSmW7Pkt44HdBaw+LnC4XIbACVwKgF88AIi2WFL+0q8QAC0mdij5YCTQOgjAWD4Gy6-CjLxijDqdjzVqTqqg8zyH6yr9iLjXDXB3azW4SiK2HYBXIPIrImELdgfMYB2s1notDFkUzqiXRBX1+1PcoJQy5ESFGo7Knju1QNR6GUptNoJecbQPseJNgi3KgpKQaNJGqNyV2cFC0HagE9c22UoDTfjayftdori90cVUSLtZ96UJ+NFFGlhXsPXe0d1do1WA-aojzRW0UHW9ipIe3IR0mpOwPFyBQ9G0gufCSj7Z2824RkNJ3ViPJFEfCSJkC4Fl2Rk9eTbZHJ2-1bWeBUQC9E9IyMAn1i4iaKRaGzqSKo7wOeRpYfAin8Rs27mioYKyTqk5i62+zRoFIqp2flwJzlSuec5cQKrlkedNeqOeGL40Psplxj-HcIumWL6TtZc89lyuvzkl-IMhVIFTk8lpoBaG-cdzXpU6PZl3ulmwoq+bqr2RaZNT2dSaGoO2q7NUf2eiHbEO7eQ9qpP7KBX6IRXZP3iQeSieSA2JIs2UyUt5Oc20XJ6Lq+c7Hz3Ra9UabLcdpuvP4hXEin+TrweRkZpyCyfcT3njPCpMX4uXKUMBuncGzDMCp1muT6Pi3CA+mB+n0BEP8roqdjyJvDQMVt6Fq34Pmdw-EdjUPyIsfluzJKvbk1CfOCY9OrbNQwdeEdAwY3NzYfWvD1KjOqGjMyTkcoKzYCJxMVAwNeHIHbdfLVQJW9IfXfOubDWve4fpfsQEfcYnJIb8KzRvJVaocpIwd4RlJLIAA */
id: 'routingStream',
context: ({ input }) => ({
currentRuleId: null,
@@ -126,6 +144,8 @@ export const streamRoutingMachine = setup({
initialRouting: [],
routing: [],
suggestedRuleId: null,
+ editingSuggestionIndex: null,
+ editedSuggestion: null,
}),
initial: 'initializing',
states: {
@@ -173,6 +193,24 @@ export const streamRoutingMachine = setup({
target: '#ready.reviewSuggestedRule',
actions: [{ type: 'storeSuggestedRuleId', params: ({ event }) => event }],
},
+ 'suggestion.edit': {
+ target: '#ready.editingSuggestedRule',
+ actions: enqueueActions(({ enqueue, event }) => {
+ enqueue({ type: 'storeEditingSuggestion', params: event });
+
+ // Set the preview for the suggestion being edited
+ enqueue.sendTo('routingSamplesMachine', {
+ type: 'routingSamples.setSelectedPreview',
+ preview: { type: 'suggestion', name: event.suggestion.name, index: event.index },
+ });
+
+ // Update condition for preview
+ enqueue.sendTo('routingSamplesMachine', {
+ type: 'routingSamples.updateCondition',
+ condition: event.suggestion.condition,
+ });
+ }),
+ },
},
invoke: {
id: 'routingSamplesMachine',
@@ -472,6 +510,88 @@ export const streamRoutingMachine = setup({
},
},
},
+ editingSuggestedRule: {
+ id: 'editingSuggestedRule',
+ initial: 'editing',
+ exit: [{ type: 'clearEditingSuggestion' }],
+ states: {
+ editing: {
+ on: {
+ 'suggestion.changeName': {
+ actions: enqueueActions(({ context, enqueue, event }) => {
+ enqueue({
+ type: 'updateEditedSuggestion',
+ params: { updates: { name: event.name } },
+ });
+
+ // Update the preview name (without triggering refetch)
+ enqueue.sendTo('routingSamplesMachine', {
+ type: 'routingSamples.updatePreviewName',
+ name: event.name,
+ });
+ }),
+ },
+ 'suggestion.changeCondition': {
+ actions: enqueueActions(({ enqueue, event }) => {
+ enqueue({
+ type: 'updateEditedSuggestion',
+ params: { updates: { condition: event.condition } },
+ });
+
+ // Update the condition for preview (triggers refetch)
+ enqueue.sendTo('routingSamplesMachine', {
+ type: 'routingSamples.updateCondition',
+ condition: event.condition,
+ });
+ }),
+ },
+ 'routingRule.change': {
+ actions: enqueueActions(({ enqueue, event }) => {
+ if (event.routingRule.where) {
+ enqueue({
+ type: 'updateEditedSuggestion',
+ params: { updates: { condition: event.routingRule.where } },
+ });
+
+ enqueue.sendTo('routingSamplesMachine', {
+ type: 'routingSamples.updateCondition',
+ condition: event.routingRule.where,
+ });
+ }
+ }),
+ },
+ 'routingRule.cancel': {
+ target: '#ready.idle',
+ actions: [
+ { type: 'clearEditingSuggestion' },
+ sendTo('routingSamplesMachine', {
+ type: 'routingSamples.setSelectedPreview',
+ preview: undefined,
+ }),
+ sendTo('routingSamplesMachine', {
+ type: 'routingSamples.updateCondition',
+ condition: undefined,
+ }),
+ ],
+ },
+ 'suggestion.saveSuggestion': {
+ target: '#ready.idle',
+ actions: [
+ { type: 'clearEditingSuggestion' },
+ sendTo('routingSamplesMachine', {
+ type: 'routingSamples.setSelectedPreview',
+ preview: undefined,
+ }),
+ sendTo('routingSamplesMachine', {
+ type: 'routingSamples.updateCondition',
+ condition: undefined,
+ }),
+ ],
+ },
+ },
+ },
+ },
+ },
},
},
},
diff --git a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/state_management/stream_routing_state_machine/types.ts b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/state_management/stream_routing_state_machine/types.ts
index cd67eeb45c85c..24ce300ca24e8 100644
--- a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/state_management/stream_routing_state_machine/types.ts
+++ b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/state_management/stream_routing_state_machine/types.ts
@@ -16,6 +16,7 @@ import type { StreamsTelemetryClient } from '../../../../../telemetry/client';
import type { RoutingDefinitionWithUIAttributes } from '../../types';
import type { DocumentMatchFilterOptions } from '.';
import type { RoutingSamplesContext } from './routing_samples_state_machine';
+import type { PartitionSuggestion } from '../../review_suggestions_form/use_review_suggestions_form';
export interface StreamRoutingServiceDependencies {
forkSuccessNofitier: (streamName: string) => void;
@@ -37,6 +38,8 @@ export interface StreamRoutingContext {
initialRouting: RoutingDefinitionWithUIAttributes[];
routing: RoutingDefinitionWithUIAttributes[];
suggestedRuleId: string | null;
+ editingSuggestionIndex: number | null;
+ editedSuggestion: PartitionSuggestion | null;
}
export type StreamRoutingEvent =
@@ -58,4 +61,8 @@ export type StreamRoutingEvent =
index: number;
toggle?: boolean;
}
- | { type: 'routingRule.reviewSuggested'; id: string };
+ | { type: 'routingRule.reviewSuggested'; id: string }
+ | { type: 'suggestion.edit'; index: number; suggestion: PartitionSuggestion }
+ | { type: 'suggestion.changeName'; name: string }
+ | { type: 'suggestion.changeCondition'; condition: Condition }
+ | { type: 'suggestion.saveSuggestion' };
diff --git a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/state_management/stream_routing_state_machine/use_stream_routing.tsx b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/state_management/stream_routing_state_machine/use_stream_routing.tsx
index a7ad303e6b879..1b840d1175491 100644
--- a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/state_management/stream_routing_state_machine/use_stream_routing.tsx
+++ b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/state_management/stream_routing_state_machine/use_stream_routing.tsx
@@ -10,6 +10,7 @@ import { createActorContext, useSelector } from '@xstate5/react';
import { createConsoleInspector } from '@kbn/xstate-utils';
import { waitFor } from 'xstate5';
import type { RoutingDefinition } from '@kbn/streams-schema';
+import type { Condition } from '@kbn/streamlang';
import {
streamRoutingMachine,
createStreamRoutingMachineImplementations,
@@ -21,6 +22,7 @@ import type {
RoutingSamplesActorRef,
RoutingSamplesActorSnapshot,
} from './routing_samples_state_machine';
+import type { PartitionSuggestion } from '../../review_suggestions_form/use_review_suggestions_form';
const consoleInspector = createConsoleInspector();
@@ -57,7 +59,18 @@ export const useStreamRoutingEvents = () => {
},
forkStream: async (routingRule?: RoutingDefinition) => {
service.send({ type: 'routingRule.fork', routingRule });
- await waitFor(service, (snapshot) => snapshot.matches({ ready: 'idle' }));
+
+ await waitFor(
+ service,
+ (snapshot) =>
+ snapshot.matches({ ready: 'idle' }) ||
+ snapshot.matches({ ready: { reviewSuggestedRule: 'reviewing' } })
+ );
+
+ const finalSnapshot = service.getSnapshot();
+ return {
+ success: finalSnapshot.matches({ ready: 'idle' }),
+ };
},
saveChanges: () => {
service.send({ type: 'routingRule.save' });
@@ -68,6 +81,18 @@ export const useStreamRoutingEvents = () => {
reviewSuggestedRule: (id: string) => {
service.send({ type: 'routingRule.reviewSuggested', id });
},
+ editSuggestion: (index: number, suggestion: PartitionSuggestion) => {
+ service.send({ type: 'suggestion.edit', index, suggestion });
+ },
+ changeSuggestionName: (name: string) => {
+ service.send({ type: 'suggestion.changeName', name });
+ },
+ changeSuggestionCondition: (condition: Condition) => {
+ service.send({ type: 'suggestion.changeCondition', condition });
+ },
+ saveEditedSuggestion: () => {
+ service.send({ type: 'suggestion.saveSuggestion' });
+ },
}),
[service]
);
diff --git a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/stream_name_form_row.tsx b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/stream_name_form_row.tsx
index 2ce55aed84d29..4804f2c68bb31 100644
--- a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/stream_name_form_row.tsx
+++ b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_routing/stream_name_form_row.tsx
@@ -14,6 +14,8 @@ interface StreamNameFormRowProps {
onChange?: (value: string) => void;
disabled?: boolean;
autoFocus?: boolean;
+ error?: string;
+ isInvalid?: boolean;
}
const MAX_NAME_LENGTH = 200;
@@ -23,6 +25,8 @@ export function StreamNameFormRow({
onChange = () => {},
disabled = false,
autoFocus = false,
+ error,
+ isInvalid = false,
}: StreamNameFormRowProps) {
const helpText =
value.length >= MAX_NAME_LENGTH && !disabled
@@ -41,6 +45,8 @@ export function StreamNameFormRow({
defaultMessage: 'Stream name',
})}
helpText={helpText}
+ error={error}
+ isInvalid={isInvalid}
>
onChange(e.target.value)}
maxLength={200}
+ isInvalid={isInvalid}
/>
);