Skip to content

Commit

Permalink
feat(step-functions): support JSONata
Browse files Browse the repository at this point in the history
  • Loading branch information
WinterYukky committed Dec 1, 2024
1 parent 77fbddf commit faa1d61
Show file tree
Hide file tree
Showing 25 changed files with 1,016 additions and 369 deletions.
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);
}

/**
* 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.
*
* @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
103 changes: 77 additions & 26 deletions packages/aws-cdk-lib/aws-stepfunctions/lib/states/distributed-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,16 @@ import { Construct } from 'constructs';
import { ItemBatcher } from './distributed-map/item-batcher';
import { IItemReader } from './distributed-map/item-reader';
import { ResultWriter } from './distributed-map/result-writer';
import { MapBase, MapBaseProps } from './map-base';
import { MapBase, MapBaseJsonataOptions, MapBaseJsonPathOptions, MapBaseOptions } from './map-base';
import { Annotations } from '../../../core';
import { FieldUtils } from '../fields';
import { StateGraph } from '../state-graph';
import { StateMachineType } from '../state-machine';
import { CatchProps, IChainable, INextable, ProcessorConfig, ProcessorMode, RetryProps } from '../types';
import { CatchProps, IChainable, INextable, ProcessorConfig, ProcessorMode, QueryLanguage, RetryProps } from '../types';

const DISTRIBUTED_MAP_SYMBOL = Symbol.for('@aws-cdk/aws-stepfunctions.DistributedMap');

/**
* Properties for configuring a Distribute Map state
*/
export interface DistributedMapProps extends MapBaseProps {
interface DistributedMapBaseProps extends MapBaseOptions {
/**
* MapExecutionType
*
Expand Down Expand Up @@ -44,15 +41,6 @@ export interface DistributedMapProps extends MapBaseProps {
*/
readonly toleratedFailurePercentage?: number;

/**
* ToleratedFailurePercentagePath
*
* Percentage of failed items to tolerate in a Map Run, as JsonPath
*
* @default - No toleratedFailurePercentagePath
*/
readonly toleratedFailurePercentagePath?: string;

/**
* ToleratedFailureCount
*
Expand All @@ -62,15 +50,6 @@ export interface DistributedMapProps extends MapBaseProps {
*/
readonly toleratedFailureCount?: number;

/**
* ToleratedFailureCountPath
*
* Number of failed items to tolerate in a Map Run, as JsonPath
*
* @default - No toleratedFailureCountPath
*/
readonly toleratedFailureCountPath?: string;

/**
* Label
*
Expand All @@ -95,6 +74,43 @@ export interface DistributedMapProps extends MapBaseProps {
readonly itemBatcher?: ItemBatcher;
}

interface DistributedMapJsonPathOptions extends MapBaseJsonPathOptions {
/**
* ToleratedFailurePercentagePath
*
* Percentage of failed items to tolerate in a Map Run, as JsonPath
*
* @default - No toleratedFailurePercentagePath
*/
readonly toleratedFailurePercentagePath?: string;

/**
* ToleratedFailureCountPath
*
* Number of failed items to tolerate in a Map Run, as JsonPath
*
* @default - No toleratedFailureCountPath
*/
readonly toleratedFailureCountPath?: string;
}

interface DistributedMapJsonataOptions extends MapBaseJsonataOptions {}

/**
* Properties for configuring a Distribute Map state that using JSONPath
*/
export interface DistributedMapJsonPathProps extends DistributedMapBaseProps, DistributedMapJsonPathOptions {}

/**
* Properties for configuring a Distribute Map state that using JSONata
*/
export interface DistributedMapJsonataProps extends DistributedMapBaseProps, DistributedMapJsonataOptions {}

/**
* Properties for configuring a Distribute Map state
*/
export interface DistributedMapProps extends DistributedMapBaseProps, DistributedMapJsonPathOptions, DistributedMapJsonataOptions {}

/**
* Define a Distributed Mode Map state in the state machine
*
Expand All @@ -109,6 +125,41 @@ export interface DistributedMapProps extends MapBaseProps {
* @see https://docs.aws.amazon.com/step-functions/latest/dg/concepts-asl-use-map-state-distributed.html
*/
export class DistributedMap extends MapBase implements INextable {
/**
* Define a Distributed Mode Map state using JSONPath in the state machine
*
* A `Map` state can be used to run a set of steps for each element of an input array.
* A Map state will execute the same steps for multiple entries of an array in the state input.
*
* While the Parallel state executes multiple branches of steps using the same input, a Map state
* will execute the same steps for multiple entries of an array in the state input.
*
* A `Map` state in `Distributed` mode will execute a child workflow for each iteration of the Map state.
* This serves to increase concurrency and allows for larger workloads to be run in a single state machine.
* @see https://docs.aws.amazon.com/step-functions/latest/dg/concepts-asl-use-map-state-distributed.html
*/
public static jsonPath(scope: Construct, id: string, props: DistributedMapJsonPathProps = {}) {
return new DistributedMap(scope, id, props);
}
/**
* Define a Distributed Mode Map state using JSONata in the state machine
*
* A `Map` state can be used to run a set of steps for each element of an input array.
* A Map state will execute the same steps for multiple entries of an array in the state input.
*
* While the Parallel state executes multiple branches of steps using the same input, a Map state
* will execute the same steps for multiple entries of an array in the state input.
*
* A `Map` state in `Distributed` mode will execute a child workflow for each iteration of the Map state.
* This serves to increase concurrency and allows for larger workloads to be run in a single state machine.
* @see https://docs.aws.amazon.com/step-functions/latest/dg/concepts-asl-use-map-state-distributed.html
*/
public static jsonata(scope: Construct, id: string, props: DistributedMapJsonataProps = {}) {
return new DistributedMap(scope, id, {
...props,
queryLanguage: QueryLanguage.JSONATA,
});
}
/**
* Return whether the given object is a DistributedMap.
*/
Expand Down Expand Up @@ -239,8 +290,8 @@ export class DistributedMap extends MapBase implements INextable {
/**
* Return the Amazon States Language object for this state
*/
public toStateJson(): object {
let rendered: any = super.toStateJson();
public toStateJson(stateMachineQueryLanguage?: QueryLanguage): object {
let rendered: any = super.toStateJson(stateMachineQueryLanguage);
if (rendered.ItemProcessor.ProcessorConfig.ExecutionType) {
Annotations.of(this).addWarningV2('@aws-cdk/aws-stepfunctions:propertyIgnored', 'Property \'ProcessorConfig.executionType\' is ignored, use the \'mapExecutionType\' in the \'DistributedMap\' class instead.');
}
Expand Down
Loading

0 comments on commit faa1d61

Please sign in to comment.