Skip to content

Commit

Permalink
Merge branch 'main' into updateDocs
Browse files Browse the repository at this point in the history
  • Loading branch information
mergify[bot] authored May 30, 2024
2 parents e74fc34 + 5ec3ec9 commit 9ce288f
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 9 deletions.
10 changes: 10 additions & 0 deletions packages/aws-cdk-lib/aws-stepfunctions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,16 @@ const fail = new sfn.Fail(this, 'Fail', {
});
```

You can also use an intrinsic function that returns a string to specify CausePath and ErrorPath.
The available functions include States.Format, States.JsonToString, States.ArrayGetItem, States.Base64Encode, States.Base64Decode, States.Hash, and States.UUID.

```ts
const fail = new sfn.Fail(this, 'Fail', {
errorPath: sfn.JsonPath.format('error: {}.', sfn.JsonPath.stringAt('$.someError')),
causePath: "States.Format('cause: {}.', $.someCause)",
});
```

### Map

A `Map` state can be used to run a set of steps for each element of an input array.
Expand Down
56 changes: 53 additions & 3 deletions packages/aws-cdk-lib/aws-stepfunctions/lib/states/fail.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Construct } from 'constructs';
import { StateType } from './private/state-type';
import { renderJsonPath, State } from './state';
import { Token } from '../../../core';
import { INextable } from '../types';

/**
Expand Down Expand Up @@ -31,6 +32,9 @@ export interface FailProps {
/**
* JsonPath expression to select part of the state to be the error to this state.
*
* You can also use an intrinsic function that returns a string to specify this property.
* The allowed functions include States.Format, States.JsonToString, States.ArrayGetItem, States.Base64Encode, States.Base64Decode, States.Hash, and States.UUID.
*
* @default - No error path
*/
readonly errorPath?: string;
Expand All @@ -45,6 +49,9 @@ export interface FailProps {
/**
* JsonPath expression to select part of the state to be the cause to this state.
*
* You can also use an intrinsic function that returns a string to specify this property.
* The allowed functions include States.Format, States.JsonToString, States.ArrayGetItem, States.Base64Encode, States.Base64Decode, States.Hash, and States.UUID.
*
* @default - No cause path
*/
readonly causePath?: string;
Expand All @@ -56,6 +63,16 @@ export interface FailProps {
* Reaching a Fail state terminates the state execution in failure.
*/
export class Fail extends State {
private static allowedIntrinsics = [
'States.Format',
'States.JsonToString',
'States.ArrayGetItem',
'States.Base64Encode',
'States.Base64Decode',
'States.Hash',
'States.UUID',
];

public readonly endStates: INextable[] = [];

private readonly error?: string;
Expand All @@ -80,9 +97,42 @@ export class Fail extends State {
Type: StateType.FAIL,
Comment: this.comment,
Error: this.error,
ErrorPath: renderJsonPath(this.errorPath),
ErrorPath: this.isIntrinsicString(this.errorPath) ? this.errorPath : renderJsonPath(this.errorPath),
Cause: this.cause,
CausePath: renderJsonPath(this.causePath),
CausePath: this.isIntrinsicString(this.causePath) ? this.causePath : renderJsonPath(this.causePath),
};
}
}

/**
* Validate this state
*/
protected validateState(): string[] {
const errors = super.validateState();

if (this.errorPath && this.isIntrinsicString(this.errorPath) && !this.isAllowedIntrinsic(this.errorPath)) {
errors.push(`You must specify a valid intrinsic function in errorPath. Must be one of ${Fail.allowedIntrinsics.join(', ')}`);
}

if (this.causePath && this.isIntrinsicString(this.causePath) && !this.isAllowedIntrinsic(this.causePath)) {
errors.push(`You must specify a valid intrinsic function in causePath. Must be one of ${Fail.allowedIntrinsics.join(', ')}`);
}

if (this.error && this.errorPath) {
errors.push('Fail state cannot have both error and errorPath');
}

if (this.cause && this.causePath) {
errors.push('Fail state cannot have both cause and causePath');
}

return errors;
}

private isIntrinsicString(jsonPath?: string): boolean {
return !Token.isUnresolved(jsonPath) && !jsonPath?.startsWith('$');
}

private isAllowedIntrinsic(intrinsic: string): boolean {
return Fail.allowedIntrinsics.some(allowed => intrinsic.startsWith(allowed));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,88 @@ describe('State Machine Resources', () => {
});
}),

test.each([
[
"States.Format('error: {}.', $.error)",
"States.Format('cause: {}.', $.cause)",
],
[
stepfunctions.JsonPath.format('error: {}.', stepfunctions.JsonPath.stringAt('$.error')),
stepfunctions.JsonPath.format('cause: {}.', stepfunctions.JsonPath.stringAt('$.cause')),
],
])('Fail should render ErrorPath / CausePath correctly when specifying ErrorPath / CausePath using intrinsics', (errorPath, causePath) => {
// GIVEN
const app = new cdk.App();
const stack = new cdk.Stack(app);
const fail = new stepfunctions.Fail(stack, 'Fail', {
errorPath,
causePath,
});

// WHEN
const failState = stack.resolve(fail.toStateJson());

// THEN
expect(failState).toStrictEqual({
CausePath: "States.Format('cause: {}.', $.cause)",
ErrorPath: "States.Format('error: {}.', $.error)",
Type: 'Fail',
});
expect(() => app.synth()).not.toThrow();
}),

test('fails in synthesis if error and errorPath are defined in Fail state', () => {
// GIVEN
const app = new cdk.App();
const stack = new cdk.Stack(app);

// WHEN
new stepfunctions.Fail(stack, 'Fail', {
error: 'error',
errorPath: '$.error',
});

expect(() => app.synth()).toThrow(/Fail state cannot have both error and errorPath/);
}),

test('fails in synthesis if cause and causePath are defined in Fail state', () => {
// GIVEN
const app = new cdk.App();
const stack = new cdk.Stack(app);

// WHEN
new stepfunctions.Fail(stack, 'Fail', {
cause: 'cause',
causePath: '$.cause',
});

expect(() => app.synth()).toThrow(/Fail state cannot have both cause and causePath/);
}),

test.each([
'States.Array($.Id)',
'States.ArrayPartition($.inputArray, 4)',
'States.ArrayContains($.inputArray, $.lookingFor)',
'States.ArrayRange(1, 9, 2)',
'States.ArrayLength($.inputArray)',
'States.JsonMerge($.json1, $.json2, false)',
'States.StringToJson($.escapedJsonString)',
'plainString',
])('fails in synthesis if specifying invalid intrinsic functions in the causePath and errorPath (%s)', (intrinsic) => {
// GIVEN
const app = new cdk.App();
const stack = new cdk.Stack(app);

// WHEN
new stepfunctions.Fail(stack, 'Fail', {
causePath: intrinsic,
errorPath: intrinsic,
});

expect(() => app.synth()).toThrow(/You must specify a valid intrinsic function in causePath. Must be one of States.Format, States.JsonToString, States.ArrayGetItem, States.Base64Encode, States.Base64Decode, States.Hash, States.UUID/);
expect(() => app.synth()).toThrow(/You must specify a valid intrinsic function in errorPath. Must be one of States.Format, States.JsonToString, States.ArrayGetItem, States.Base64Encode, States.Base64Decode, States.Hash, States.UUID/);
}),

testDeprecated('Task should render InputPath / Parameters / OutputPath correctly', () => {
// GIVEN
const stack = new cdk.Stack();
Expand Down Expand Up @@ -721,7 +803,6 @@ describe('State Machine Resources', () => {
],
});
});

});

interface FakeTaskProps extends stepfunctions.TaskStateBaseProps {
Expand Down
7 changes: 2 additions & 5 deletions tools/@aws-cdk/cdk-build-tools/config/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,8 @@ module.exports = {
},
],
},

// Limit workers to a reasonable fixed number. If we scale in the number of available CPUs, we will explode
// our memory limit on the CodeBuild instance that has 72 CPUs.
maxWorkers: Math.min(8, cpus().length - 1),

// Jest is resource greedy so this shouldn't be more than 50%
maxWorkers: '50%',
testEnvironment: 'node',
coverageThreshold: {
global: {
Expand Down

0 comments on commit 9ce288f

Please sign in to comment.