Skip to content

Commit d0caa48

Browse files
committed
Add unified collectDescendantStepIds() util
1 parent ee7db33 commit d0caa48

7 files changed

Lines changed: 65 additions & 57 deletions

File tree

x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/interactive_mode_machine/interactive_mode_machine.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ import { stepMachine } from '../steps_state_machine';
3131
import type { StepParentActor } from '../steps_state_machine/types';
3232
import { hasErrorsInParentSnapshot } from '../stream_enrichment_state_machine/selectors';
3333
import {
34-
collectDescendantIds,
3534
findInsertIndex,
3635
insertAtIndex,
3736
reorderSteps,
3837
} from '../stream_enrichment_state_machine/utils';
38+
import { collectDescendantStepIds } from '../utils';
3939
import { selectWhetherAnyProcessorBeforePersisted } from './selectors';
4040
import {
4141
createNotifySuggestionFailureNotifier,
@@ -178,7 +178,8 @@ export const interactiveModeMachine = setup({
178178
}
179179
),
180180
deleteStep: assign(({ context }, params: { id: string }) => {
181-
const idsToDelete = collectDescendantIds(params.id, context.stepRefs);
181+
const steps = context.stepRefs.map((ref) => ref.getSnapshot().context.step);
182+
const idsToDelete = collectDescendantStepIds(steps, params.id);
182183
idsToDelete.add(params.id);
183184
return {
184185
stepRefs: context.stepRefs.filter((proc) => !idsToDelete.has(proc.id)),

x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/interactive_mode_machine/utils.ts

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import type { SampleDocumentWithUIAttributes } from '../simulation_state_machine
1515
import type { StepActorRef, StepInput, StepParentActor } from '../steps_state_machine';
1616
import { isStepUnderEdit } from '../steps_state_machine';
1717
import type { InteractiveModeContext } from './types';
18+
import { collectDescendantStepIds } from '../utils';
1819

1920
export type StepSpawner = (
2021
src: 'stepMachine',
@@ -64,7 +65,8 @@ export function getStepsForSimulation({
6465

6566
// Truncate to the selected condition subtree (and everything before it)
6667
if (selectedConditionId) {
67-
const conditionAndDescendants = collectDescendantIds(selectedConditionId, stepRefs);
68+
const steps = stepRefs.map((ref) => ref.getSnapshot().context.step);
69+
const conditionAndDescendants = collectDescendantStepIds(steps, selectedConditionId);
6870

6971
conditionAndDescendants.add(selectedConditionId);
7072

@@ -118,17 +120,3 @@ export function getActiveDataSourceSamplesFromParent(
118120
document: doc,
119121
}));
120122
}
121-
122-
function collectDescendantIds(id: string, stepRefs: StepActorRef[]): Set<string> {
123-
const ids = new Set<string>();
124-
function collect(currentId: string) {
125-
stepRefs
126-
.filter((step) => step.getSnapshot().context.step.parentId === currentId)
127-
.forEach((child) => {
128-
ids.add(child.id);
129-
collect(child.id);
130-
});
131-
}
132-
collect(id);
133-
return ids;
134-
}

x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/simulation_state_machine/utils.ts

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import type {
2121
} from '../../../schema_editor/types';
2222
import { isSchemaFieldTyped } from '../../../schema_editor/types';
2323
import { convertToFieldDefinitionConfig } from '../../../schema_editor/utils';
24+
import { collectDescendantStepIds } from '../utils';
2425
import type { PreviewDocsFilterOption } from './simulation_documents_search';
2526
import type { DetectedField, Simulation, SimulationContext } from './types';
2627

@@ -53,23 +54,6 @@ export function getUniqueDetectedFields(detectedFields: DetectedField[] = []) {
5354
return uniq(detectedFields.map((field) => field.name));
5455
}
5556

56-
/**
57-
* Recursively collects all descendant step IDs
58-
* for a given parent step ID.
59-
*/
60-
function collectDescendantStepIds(steps: StreamlangStepWithUIAttributes[], parentId: string) {
61-
const ids = new Set<string>();
62-
63-
steps.forEach((step) => {
64-
if (step.parentId === parentId) {
65-
ids.add(step.customIdentifier);
66-
collectDescendantStepIds(steps, step.customIdentifier).forEach((childId) => ids.add(childId));
67-
}
68-
});
69-
70-
return ids;
71-
}
72-
7357
/**
7458
* Recursively collects all descendant processor IDs
7559
* for a given condition step ID.

x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/state_management/stream_enrichment_state_machine/utils.ts

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
getUnmappedSchemaFields,
2929
} from '../simulation_state_machine';
3030
import type { StepActorRef } from '../steps_state_machine';
31+
import { collectDescendantStepIds } from '../utils';
3132
import type { StreamEnrichmentContextType } from './types';
3233

3334
export const defaultLatestSamplesDataSource: LatestSamplesDataSource = {
@@ -145,7 +146,8 @@ export const spawnDataSource = <
145146
* Takes into account nested conditions and their descendants.
146147
*/
147148
function findNewSiblingStepIndex(stepRefs: StepActorRef[], parentId: string): number {
148-
const descendantStepIds = collectDescendantIds(parentId, stepRefs);
149+
const steps = stepRefs.map((ref) => ref.getSnapshot().context.step);
150+
const descendantStepIds = collectDescendantStepIds(steps, parentId);
149151
const lastDescendantId = Array.from(descendantStepIds).at(-1);
150152

151153
const lastDescendantIndex = stepRefs.findIndex((stepRef) => {
@@ -162,7 +164,6 @@ function findNewSiblingStepIndex(stepRefs: StepActorRef[], parentId: string): nu
162164
/* Find insert index based on step hierarchy */
163165
export function findInsertIndex(stepRefs: StepActorRef[], parentId: string | null): number {
164166
// Find the index of the parent step
165-
// debugger;
166167
const parentIndex = parentId ? stepRefs.findIndex((step) => step.id === parentId) : -1;
167168

168169
// Find the last index of any step with the same parentId
@@ -203,7 +204,8 @@ export function reorderSteps(
203204
direction: 'up' | 'down'
204205
): StepActorRef[] {
205206
// 1. Collect all descendant ids for the block to move
206-
const children = collectDescendantIds(stepId, stepRefs);
207+
const steps = stepRefs.map((ref) => ref.getSnapshot().context.step);
208+
const children = collectDescendantStepIds(steps, stepId);
207209
const allBlockIds = new Set([stepId, ...children]);
208210

209211
// 2. Find the start and end index of the block in the original array
@@ -246,7 +248,7 @@ export function reorderSteps(
246248
// Find the end of this next block in withoutBlock
247249
let candidateBlockEnd = withoutBlock.findIndex((step) => step.id === candidate.id);
248250
// Find the last descendant of this block
249-
const candidateDescendants = collectDescendantIds(candidate.id, stepRefs);
251+
const candidateDescendants = collectDescendantStepIds(steps, candidate.id);
250252
if (candidateDescendants.size > 0) {
251253
const lastDescendantId = Array.from(candidateDescendants).pop();
252254
const lastDescendantIdx = withoutBlock.findIndex((step) => step.id === lastDescendantId);
@@ -263,20 +265,6 @@ export function reorderSteps(
263265
}
264266
}
265267

266-
export function collectDescendantIds(id: string, stepRefs: StepActorRef[]): Set<string> {
267-
const ids = new Set<string>();
268-
function collect(currentId: string) {
269-
stepRefs
270-
.filter((step) => step.getSnapshot().context.step.parentId === currentId)
271-
.forEach((child) => {
272-
ids.add(child.id);
273-
collect(child.id);
274-
});
275-
}
276-
collect(id);
277-
return ids;
278-
}
279-
280268
export type RootLevelMap = Map<string, string>;
281269
// Maps every step to their corresponding root level step
282270
export function getRootLevelStepsMap(stepRefs: StepActorRef[]): Map<string, string> {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import type { StreamlangStepWithUIAttributes } from '@kbn/streamlang';
9+
import { collectDescendantStepIds } from './utils';
10+
11+
const makeStep = (
12+
id: string,
13+
parentId: StreamlangStepWithUIAttributes['parentId'] = null
14+
): StreamlangStepWithUIAttributes =>
15+
({
16+
customIdentifier: id,
17+
parentId,
18+
} as StreamlangStepWithUIAttributes);
19+
20+
const steps: StreamlangStepWithUIAttributes[] = [
21+
makeStep('root'),
22+
makeStep('child1', 'root'),
23+
makeStep('child2', 'root'),
24+
makeStep('grandchild1', 'child1'),
25+
makeStep('grandchild2', 'child1'),
26+
makeStep('greatGrandchild', 'grandchild1'),
27+
];
28+
29+
describe('collectDescendantStepIds', () => {
30+
it('returns all descendants for a given parent', () => {
31+
const ids = Array.from(collectDescendantStepIds(steps, 'root'));
32+
expect(ids).toEqual(['child1', 'grandchild1', 'greatGrandchild', 'grandchild2', 'child2']);
33+
});
34+
35+
it('returns only subtree descendants', () => {
36+
const ids = Array.from(collectDescendantStepIds(steps, 'child1'));
37+
expect(ids).toEqual(['grandchild1', 'greatGrandchild', 'grandchild2']);
38+
});
39+
40+
it('returns empty set when no descendants exist', () => {
41+
const ids = Array.from(collectDescendantStepIds(steps, 'leaf'));
42+
expect(ids).toEqual([]);
43+
});
44+
});

x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/steps/blocks/context_menu.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
useStreamEnrichmentSelector,
2626
} from '../../state_management/stream_enrichment_state_machine';
2727
import { selectStreamType } from '../../state_management/stream_enrichment_state_machine/selectors';
28-
import { collectDescendantIds } from '../../state_management/stream_enrichment_state_machine/utils';
28+
import { collectDescendantStepIds } from '../../state_management/utils';
2929
import type { StepConfigurationProps } from '../steps_list';
3030
import { EditStepDescriptionModal } from './action/edit_step_description_modal';
3131
import { deleteProcessorPromptOptions } from './action/prompt_options';
@@ -112,7 +112,7 @@ export const StepContextMenu: React.FC<StepContextMenuProps> = ({
112112
(snapshot) => snapshot.context.selectedConditionId
113113
);
114114
const stepRefs = useInteractiveModeSelector((snapshot) => snapshot.context.stepRefs);
115-
115+
const steps = stepRefs.map((ref) => ref.getSnapshot().context.step);
116116
const step = useSelector(stepRef, (snapshot) => snapshot.context.step);
117117

118118
const streamType = useStreamEnrichmentSelector((snapshot) => selectStreamType(snapshot.context));
@@ -139,7 +139,7 @@ export const StepContextMenu: React.FC<StepContextMenuProps> = ({
139139
};
140140

141141
const getChildStepsLength = () => {
142-
return !isWhere ? 0 : collectDescendantIds(step.customIdentifier, stepRefs).size;
142+
return !isWhere ? 0 : collectDescendantStepIds(steps, step.customIdentifier).size;
143143
};
144144

145145
const deletePromptOptions = !isWhere

x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_enrichment/steps/blocks/where/index.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import {
2626
useSimulatorSelector,
2727
useStreamEnrichmentEvents,
2828
} from '../../../state_management/stream_enrichment_state_machine';
29-
import { collectDescendantIds } from '../../../state_management/stream_enrichment_state_machine/utils';
29+
import { collectDescendantStepIds } from '../../../state_management/utils';
3030
import { getStepPanelColour } from '../../../utils';
3131
import type { StepConfigurationProps } from '../../steps_list';
3232
import { StepsListItem } from '../../steps_list';
@@ -78,7 +78,10 @@ export const WhereBlock = (props: StepConfigurationProps) => {
7878

7979
// Only gather these for the summary if the block is collapsed
8080
const descendantIds = !isExpanded
81-
? collectDescendantIds(step.customIdentifier, stepRefs)
81+
? collectDescendantStepIds(
82+
stepRefs.map((ref) => ref.getSnapshot().context.step),
83+
step.customIdentifier
84+
)
8285
: undefined;
8386

8487
useEffect(() => {

0 commit comments

Comments
 (0)