Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(stepfunctions): add support JSONata and variables #32343

Open
wants to merge 68 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
faa1d61
feat(step-functions): support JSONata
WinterYukky Dec 1, 2024
806b0d0
WIP: update SFn doc
WinterYukky Dec 1, 2024
b61b834
implement jsonata for lambda
WinterYukky Dec 2, 2024
63b636e
implement jsonata for apigateway
WinterYukky Dec 2, 2024
40e1cca
add util functions
WinterYukky Dec 2, 2024
bd19aad
implement JSONata for glue task
WinterYukky Dec 2, 2024
a48d3c4
implement JSONata for bedrock task
WinterYukky Dec 2, 2024
a4fb2ba
implement JSONata for athena
WinterYukky Dec 2, 2024
f454e48
implement JSONata for aws-sdk
WinterYukky Dec 2, 2024
b46bdfe
implement JSONata for batch
WinterYukky Dec 2, 2024
f4f333c
implement JSONata for codebuild
WinterYukky Dec 2, 2024
4207156
implement JSONata for databrew
WinterYukky Dec 2, 2024
08d810d
implement JSONata for dynamodb
WinterYukky Dec 2, 2024
49c35e0
chore: JSONPATH to JSON_PATH
WinterYukky Dec 6, 2024
05d03d2
revert: README.md
WinterYukky Dec 6, 2024
f716d28
chore: rename output to outputs
WinterYukky Dec 6, 2024
44c4562
remove extra interface
WinterYukky Dec 6, 2024
dc00241
chore: update JSONata syntax error message
WinterYukky Dec 6, 2024
f327184
chore: remove extra interface
WinterYukky Dec 6, 2024
f6f39d8
implement JSONata for databrew
WinterYukky Dec 6, 2024
37c9838
implement JSONata for ecs
WinterYukky Dec 6, 2024
112554a
implement JSONata for eks
WinterYukky Dec 6, 2024
1e54e5d
implement JSONata for emr
WinterYukky Dec 6, 2024
d92b9da
implement JSONata for emr containers
WinterYukky Dec 6, 2024
132b923
implement JSONata for eventbridge
WinterYukky Dec 6, 2024
70c7ee8
implement JSONata for eventbridge scheduler
WinterYukky Dec 6, 2024
577f4b4
implement JSONata for http
WinterYukky Dec 6, 2024
e4a26e1
implement JSONata for media convert
WinterYukky Dec 6, 2024
b3d457a
add inspect JSONPath or JSONata function
WinterYukky Dec 6, 2024
2aeddbf
implement JSONata for sagemaker
WinterYukky Dec 6, 2024
d3b83d6
implement JSONata for sns
WinterYukky Dec 6, 2024
053106e
implement JSONata for sqs
WinterYukky Dec 6, 2024
28f8968
implement JSONata for stepfunctions
WinterYukky Dec 6, 2024
d100e63
rename _whichQueryLanguage to _getActualQueryLanguage
WinterYukky Dec 6, 2024
c8a854d
Update packages/aws-cdk-lib/aws-stepfunctions/lib/condition.ts
WinterYukky Dec 6, 2024
1a3508f
Merge branch 'main' into feat/step-functions/support-jsonata
WinterYukky Dec 13, 2024
dfa9b42
update: unit tests
WinterYukky Dec 14, 2024
45613c0
Merge branch 'feat/step-functions/support-jsonata' of https://github.…
WinterYukky Dec 14, 2024
8b5284f
feat: workflow variables
WinterYukky Dec 14, 2024
a8547ab
chore: apply lint
WinterYukky Dec 14, 2024
9890e33
revert: lint rule
WinterYukky Dec 14, 2024
fe49e06
test: add unit tests to step-function-tasks
WinterYukky Dec 20, 2024
d39faf6
Merge branch 'main' into feat/step-functions/support-jsonata
WinterYukky Dec 27, 2024
a15d1de
add integ-test for JSONata state machine
WinterYukky Dec 28, 2024
631b91f
chore: fix top-level comment
WinterYukky Jan 7, 2025
e5cb3a6
fix: use JSONata expression for api endpoint
WinterYukky Jan 7, 2025
bd96139
chore: test data rename
WinterYukky Jan 7, 2025
33d5020
docs: add JSONata document
WinterYukky Jan 7, 2025
c842e74
Merge branch 'main' into feat/step-functions/support-jsonata
WinterYukky Jan 8, 2025
17f4a4f
doc: fix problems that find by rosetta
WinterYukky Jan 16, 2025
09ecab7
doc: fix missing quote
WinterYukky Jan 16, 2025
5ca1de5
Merge branch 'main' into feat/step-functions/support-jsonata
WinterYukky Jan 16, 2025
c51dc2b
Merge branch 'main' into feat/step-functions/support-jsonata
WinterYukky Jan 17, 2025
5c404ed
Merge branch 'main' into feat/step-functions/support-jsonata
WinterYukky Jan 17, 2025
6af3245
doc: revert word changes
WinterYukky Jan 17, 2025
1394899
Merge branch 'feat/step-functions/support-jsonata' of https://github.…
WinterYukky Jan 17, 2025
f0a1fb5
Merge branch 'main' into feat/step-functions/support-jsonata
WinterYukky Jan 20, 2025
9352041
chore: typo CallApiGatewayEndpoint
WinterYukky Jan 22, 2025
2366a2a
fix: wait must supports output, inputPath, outputPath
WinterYukky Jan 22, 2025
68dd4bb
Update packages/aws-cdk-lib/aws-stepfunctions/README.md
WinterYukky Jan 22, 2025
5702c3d
Update packages/aws-cdk-lib/aws-stepfunctions/README.md
WinterYukky Jan 22, 2025
3ce2b58
Apply suggestions from code review
WinterYukky Jan 22, 2025
d0547d0
test: update integ test for JSONata
WinterYukky Jan 22, 2025
78400a3
Merge branch 'feat/step-functions/support-jsonata' of https://github.…
WinterYukky Jan 22, 2025
d2b35f9
Merge branch 'main' of https://github.com/WinterYukky/aws-cdk into fe…
WinterYukky Jan 29, 2025
85aaa5f
Update packages/aws-cdk-lib/aws-stepfunctions/README.md
WinterYukky Jan 29, 2025
b838f15
fix: linter error
WinterYukky Jan 30, 2025
9c9d518
Merge branch 'main' into feat/step-functions/support-jsonata
WinterYukky Jan 30, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export interface BedrockInvokeModelOutputProps {
/**
* Properties for invoking a Bedrock Model
*/
export interface BedrockInvokeModelProps extends sfn.TaskStateBaseProps {
export interface BedrockInvokeModelProps extends sfn.TaskStateBaseJsonPathProps {

/**
* The Bedrock model that the task will invoke.
Expand Down
53 changes: 43 additions & 10 deletions packages/aws-cdk-lib/aws-stepfunctions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ const submitJob = new tasks.LambdaInvoke(this, 'Submit Job', {
outputPath: '$.guid',
});

const waitX = new sfn.Wait(this, 'Wait X Seconds', {
time: sfn.WaitTime.secondsPath('$.waitSeconds'),
const waitX = sfn.Wait.jsonata(this, 'Wait X Seconds', {
time: sfn.WaitTime.seconds('{% $states.input.waitSeconds %}'),
WinterYukky marked this conversation as resolved.
Show resolved Hide resolved
});

const getStatus = new tasks.LambdaInvoke(this, 'Get Job Status', {
Expand All @@ -50,10 +50,10 @@ const finalStatus = new tasks.LambdaInvoke(this, 'Get Final Job Status', {
const definition = submitJob
.next(waitX)
.next(getStatus)
.next(new sfn.Choice(this, 'Job Complete?')
.next(sfn.Choice.jsonata(this, 'Job Complete?')
// Look at the "status" field
.when(sfn.Condition.stringEquals('$.status', 'FAILED'), jobFailed)
.when(sfn.Condition.stringEquals('$.status', 'SUCCEEDED'), finalStatus)
.when(sfn.Condition.jsonata('{% $states.input.status = FAILED %}'), jobFailed)
.when(sfn.Condition.jsonata('{% $states.input.status = SUCCEEDED %}'), finalStatus)
.otherwise(waitX));

new sfn.StateMachine(this, 'StateMachine', {
Expand All @@ -73,7 +73,7 @@ definition. The definition is specified by its start state, and encompasses
all states reachable from the start state:

```ts
const startState = new sfn.Pass(this, 'StartState');
const startState = sfn.Pass.jsonata(this, 'StartState');

new sfn.StateMachine(this, 'StateMachine', {
definitionBody: sfn.DefinitionBody.fromChainable(startState),
Expand All @@ -100,7 +100,7 @@ Alternatively you can specify an existing step functions definition by providing

```ts
new sfn.StateMachine(this, 'StateMachineFromString', {
definitionBody: sfn.DefinitionBody.fromString('{"StartAt":"Pass","States":{"Pass":{"Type":"Pass","End":true}}}'),
definitionBody: sfn.DefinitionBody.fromString('{"StartAt":"Pass","States":{"Pass":{"Type":"Pass","QueryLanguage":"JSONata","End":true}}}'),
});

new sfn.StateMachine(this, 'StateMachineFromFile', {
Expand All @@ -117,10 +117,41 @@ gets modified by individual steps as the state machine progresses, and finally
is produced as output.

By default, the entire Data object is passed into every state, and the return data of the step
becomes new the new Data object. This behavior can be modified by supplying values for `inputPath`,
`resultSelector`, `resultPath` and `outputPath`.
becomes new the new Data object. Step Functions supports JSONPath and JSONata for these data operations. Data can be changed using the operation methods provided for each query language.

### Manipulating state machine data using inputPath, resultSelector, resultPath and outputPath
### (JSONata) Transforming data with JSONata
With JSONata, you gain a powerful open source query and expression language to select and transform data in your workflows. For a brief introduction and complete JSONata reference, see [JSONata.org documentation](https://docs.jsonata.org/overview.html). After selecting JSONata, your workflow fields will be reduced from five JSONPath fields (`InputPath`, `Parameters`, `ResultSelector`, `ResultPath`, and `OutputPath`) to only two fields: `Arguments` and `Output`. Also, you will not use `.$` on JSON object key names.

If you are new to Step Functions, you only need to know that JSONata expressions use the following syntax:

JSONata syntax: `"{% <JSONata expression> %}"`

The following code samples show a conversion from JSONPath to JSONata:

```json
// Original sample using JSONPath
{
"static": "Hello",
"title.$": "$.title",
"name.$": "$customerName", // With $customerName declared as a variable
"not-evaluated": "$customerName"
}
```

```json
// Sample after conversion to JSONata
{
"static": "Hello",
"title": "{% $states.input.title %}",
"name": "{% $customerName %}", // With $customerName declared as a variable
"not-evaluated": "$customerName"
}
```

See the official documentation on [Transforming data with JSONata in Step Functions](https://docs.aws.amazon.com/step-functions/latest/dg/transforming-data.html).


### (JSONPath) Manipulating state machine data using inputPath, resultSelector, resultPath and outputPath

These properties impact how each individual step interacts with the state machine data:

Expand Down Expand Up @@ -160,6 +191,8 @@ See the official documentation on [input and output processing in Step Functions
Tasks take parameters, whose values can be taken from the State Machine Data object. For example, your
workflow may want to start a CodeBuild with an environment variable that is taken from the State Machine data, or pass part of the State Machine Data into an AWS Lambda Function.

#### (JSONPath) Passing Parameters to Tasks

In the original JSON-based states language used by AWS Step Functions, you would
add `.$` to the end of a key to indicate that a value needs to be interpreted as
a JSON path. In the CDK API you do not change the names of any keys. Instead, you
Expand Down
25 changes: 25 additions & 0 deletions packages/aws-cdk-lib/aws-stepfunctions/lib/condition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,13 @@ export abstract class Condition {
return new NotCondition(condition);
}

/**
* JSONata expression condition
*/
public static jsonata(conditon: string): Condition {
return new JsonataCondition(conditon);
}
WinterYukky marked this conversation as resolved.
Show resolved Hide resolved

/**
* Render Amazon States Language JSON for the condition
*/
Expand Down Expand Up @@ -452,3 +459,21 @@ class NotCondition extends Condition {
};
}
}

/**
* JSONata for Condition
*/
class JsonataCondition extends Condition {
constructor(private readonly condition: string) {
super();
if (!/^{%(.*)%}$/.test(condition)) {
throw new Error(`Variable reference must be '$', start with '$.', or start with '$[', got '${condition}'`);
}
}

public renderCondition(): any {
return {
Condition: this.condition,
};
}
}
5 changes: 3 additions & 2 deletions packages/aws-cdk-lib/aws-stepfunctions/lib/state-graph.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { StateMachine } from './state-machine';
import { DistributedMap } from './states/distributed-map';
import { State } from './states/state';
import { QueryLanguage } from './types';
import * as iam from '../../aws-iam';
import { Duration } from '../../core';

Expand Down Expand Up @@ -105,10 +106,10 @@ export class StateGraph {
/**
* Return the Amazon States Language JSON for this graph
*/
public toGraphJson(): object {
public toGraphJson(queryLanguage?: QueryLanguage): object {
const states: any = {};
for (const state of this.allStates) {
states[state.stateId] = state.toStateJson();
states[state.stateId] = state.toStateJson(queryLanguage);
}

return {
Expand Down
19 changes: 16 additions & 3 deletions packages/aws-cdk-lib/aws-stepfunctions/lib/state-machine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { buildEncryptionConfiguration } from './private/util';
import { StateGraph } from './state-graph';
import { StatesMetrics } from './stepfunctions-canned-metrics.generated';
import { CfnStateMachine } from './stepfunctions.generated';
import { IChainable } from './types';
import { IChainable, QueryLanguage } from './types';
import * as cloudwatch from '../../aws-cloudwatch';
import * as iam from '../../aws-iam';
import * as logs from '../../aws-logs';
Expand Down Expand Up @@ -129,6 +129,15 @@ export interface StateMachineProps {
*/
readonly comment?: string;

/**
* The name of the query language used by the state machine.
* If the state does not contain a `queryLanguage` field,
* then it will use the query language specified in the top-level `queryLanguage` field.
WinterYukky marked this conversation as resolved.
Show resolved Hide resolved
*
* @default - JSONPATH
*/
readonly queryLanguage?: QueryLanguage;

/**
* Type of the state machine
*
Expand Down Expand Up @@ -786,9 +795,13 @@ export class ChainDefinitionBody extends DefinitionBody {
}

public bind(scope: Construct, _sfnPrincipal: iam.IPrincipal, sfnProps: StateMachineProps, graph?: StateGraph): DefinitionConfig {
const graphJson = graph!.toGraphJson();
const graphJson = graph!.toGraphJson(sfnProps.queryLanguage);
return {
definitionString: Stack.of(scope).toJsonString({ ...graphJson, Comment: sfnProps.comment }),
definitionString: Stack.of(scope).toJsonString({
...graphJson,
Comment: sfnProps.comment,
QueryLanguage: sfnProps.queryLanguage,
}),
};
}
}
76 changes: 39 additions & 37 deletions packages/aws-cdk-lib/aws-stepfunctions/lib/states/choice.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,28 @@
import { Construct } from 'constructs';
import { StateType } from './private/state-type';
import { ChoiceTransitionOptions, State } from './state';
import { ChoiceTransitionOptions, JsonataCommonOptions, JsonPathCommonOptions, State, StateBaseProps } from './state';
import { Chain } from '../chain';
import { Condition } from '../condition';
import { IChainable, INextable } from '../types';
import { IChainable, INextable, QueryLanguage } from '../types';

interface ChoiceBaseProps extends StateBaseProps {}
interface ChoiceJsonPathOptions extends JsonPathCommonOptions {}
interface ChoiceJsonataOptions extends JsonataCommonOptions {}

/**
* Properties for defining a Choice state
* Properties for defining a Choice state that using JSONPath
*/
export interface ChoiceProps {
/**
* Optional name for this state
*
* @default - The construct ID will be used as state name
*/
readonly stateName?: string;

/**
* An optional description for this state
*
* @default No comment
*/
readonly comment?: string;
export interface ChoiceJsonPathProps extends ChoiceBaseProps, ChoiceJsonPathOptions { }

/**
* JSONPath expression to select part of the state to be the input to this state.
*
* May also be the special value JsonPath.DISCARD, which will cause the effective
* input to be the empty object {}.
*
* @default $
*/
readonly inputPath?: string;
/**
* Properties for defining a Choice state that using JSONata
*/
export interface ChoiceJsonataProps extends ChoiceBaseProps, ChoiceJsonataOptions { }

/**
* JSONPath expression to select part of the state to be the output to this state.
*
* May also be the special value JsonPath.DISCARD, which will cause the effective
* output to be the empty object {}.
*
* @default $
*/
readonly outputPath?: string;
}
/**
* Properties for defining a Choice state
*/
export interface ChoiceProps extends ChoiceBaseProps, ChoiceJsonPathOptions, ChoiceJsonataOptions { }

/**
* Define a Choice in the state machine
Expand All @@ -51,6 +31,27 @@ export interface ChoiceProps {
* state.
*/
export class Choice extends State {
/**
* Define a Choice using JSONPath in the state machine
*
* A choice state can be used to make decisions based on the execution
* state.
*/
public static jsonPath(scope: Construct, id: string, props: ChoiceJsonPathProps = {}) {
return new Choice(scope, id, props);
}
/**
* Define a Choice using JSONata in the state machine
*
* A choice state can be used to make decisions based on the execution
* state.
*/
public static jsonata(scope: Construct, id: string, props: ChoiceJsonataProps = {}) {
return new Choice(scope, id, {
...props,
queryLanguage: QueryLanguage.JSONATA,
});
}
public readonly endStates: INextable[] = [];

constructor(scope: Construct, id: string, props: ChoiceProps = {}) {
Expand Down Expand Up @@ -95,9 +96,10 @@ export class Choice extends State {
/**
* Return the Amazon States Language object for this state
*/
public toStateJson(): object {
public toStateJson(queryLanguage?: QueryLanguage): object {
return {
Type: StateType.CHOICE,
...this.renderQueryLanguage(queryLanguage),
Comment: this.comment,
...this.renderInputOutput(),
...this.renderChoices(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Construct } from 'constructs';
import { Chain } from '..';
import { State } from './state';
import { Annotations } from '../../../core/';
import { CatchProps, IChainable, INextable, RetryProps } from '../types';
import { CatchProps, IChainable, INextable, QueryLanguage, RetryProps } from '../types';

/**
* Properties for defining a custom state definition
Expand Down Expand Up @@ -68,8 +68,9 @@ export class CustomState extends State implements IChainable, INextable {
/**
* Returns the Amazon States Language object for this state
*/
public toStateJson(): object {
public toStateJson(queryLanguage?: QueryLanguage): object {
const state = {
...this.renderQueryLanguage(queryLanguage),
...this.renderNextEnd(),
...this.stateJson,
...this.renderRetryCatch(),
Expand Down
Loading
Loading