Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
4 changes: 4 additions & 0 deletions packages/@aws-cdk/aws-stepfunctions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,10 @@ new stepfunctions.Parallel(this, 'All jobs')
.branch(new MyJob(this, 'Slow', { jobFlavor: 'slow' }).prefixStates());
```

A few utility functions are available to parse state machine fragments.
* `State.findReachableStates`: Retrieve the list of states reachable from a given state.
* `State.findReachableEndStates`: Retrieve the list of end or terminal states reachable from a given state.

## Activity

**Activities** represent work that is done on some non-Lambda worker pool. The
Expand Down
21 changes: 20 additions & 1 deletion packages/@aws-cdk/aws-stepfunctions/lib/states/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,29 @@ export abstract class State extends cdk.Construct implements IChainable {
}
}

/**
* Find the set of states reachable through transitions from the given start state.
* This does not retrieve states from within sub-graphs, such as states within a Parallel state's branch.
*/
public static findReachableStates(start: State, options: FindStateOptions = {}): State[] {
const visited = new Set<State>();
const ret = new Set<State>();
const queue = [start];
while (queue.length > 0) {
const state = queue.splice(0, 1)[0]!;
if (visited.has(state)) { continue; }
visited.add(state);
const outgoing = state.outgoingTransitions(options);
queue.push(...outgoing);
ret.add(state);
}
return Array.from(ret);
}

/**
* Find the set of end states states reachable through transitions from the given start state
*/
public static findReachableEndStates(start: State, options: FindStateOptions = {}) {
public static findReachableEndStates(start: State, options: FindStateOptions = {}): State[] {
const visited = new Set<State>();
const ret = new Set<State>();
const queue = [start];
Expand Down
72 changes: 72 additions & 0 deletions packages/@aws-cdk/aws-stepfunctions/test/states-language.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,78 @@ describe('States Language', () => {
expect(() => new stepfunctions.Parallel(stack, 'Parallel')
.branch(state1.next(state2))
.branch(state2)).toThrow();
}),

describe('findReachableStates', () => {

test('Can retrieve possible states from initial state', () => {
// GIVEN
const stack = new cdk.Stack();
const state1 = new stepfunctions.Pass(stack, 'State1');
const state2 = new stepfunctions.Pass(stack, 'State2');
const state3 = new stepfunctions.Pass(stack, 'State3');

const definition = state1
.next(state2)
.next(state3);

// WHEN
const states = stepfunctions.State.findReachableStates(definition.startState);

// THEN
expect(state1.id).toStrictEqual(states[0].id);
expect(state2.id).toStrictEqual(states[1].id);
expect(state3.id).toStrictEqual(states[2].id);
});

test('Does not retrieve unreachable states', () => {
// GIVEN
const stack = new cdk.Stack();
const state1 = new stepfunctions.Pass(stack, 'State1');
const state2 = new stepfunctions.Pass(stack, 'State2');
const state3 = new stepfunctions.Pass(stack, 'State3');

state1.next(state2).next(state3);

// WHEN
const states = stepfunctions.State.findReachableStates(state2);

// THEN
expect(state2.id).toStrictEqual(states[0].id);
expect(state3.id).toStrictEqual(states[1].id);
expect(states.length).toStrictEqual(2);
});

test('Works with Choice and Parallel states', () => {
// GIVEN
const stack = new cdk.Stack();
const state1 = new stepfunctions.Choice(stack, 'MainChoice');
const stateCA = new stepfunctions.Pass(stack, 'StateA');
const stateCB = new stepfunctions.Pass(stack, 'StateB');
const statePA = new stepfunctions.Pass(stack, 'ParallelA');
const statePB = new stepfunctions.Pass(stack, 'ParallelB');
const state2 = new stepfunctions.Parallel(stack, 'RunParallel');
const state3 = new stepfunctions.Pass(stack, 'FinalState');
state2.branch(statePA);
state2.branch(statePB);
state1.when(stepfunctions.Condition.stringEquals('$.myInput', 'A' ), stateCA);
state1.when(stepfunctions.Condition.stringEquals('$.myInput', 'B'), stateCB);
stateCA.next(state2);
state2.next(state3);

const definition = state1.otherwise(stateCA);

// WHEN
const statesFromStateCB = stepfunctions.State.findReachableStates(stateCB);
const statesFromState1 = stepfunctions.State.findReachableStates(definition);

// THEN
const expectedFromState1 = [state1, stateCA, stateCB, state2, state3];
for (let i = 0; i < expectedFromState1.length; i++) {
expect(statesFromState1[i].id).toStrictEqual(expectedFromState1[i].id);
}
expect(statesFromStateCB[0].id).toStrictEqual(stateCB.id);
});
});
});

Expand Down