Skip to content

Commit c840887

Browse files
authored
Merge branch 'master' into add_secret_grantwrite
2 parents aae9142 + c038848 commit c840887

25 files changed

+2491
-67
lines changed

packages/@aws-cdk/aws-stepfunctions-tasks/README.md

+60-50
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,24 @@ The following snippet invokes a Lambda Function with the state input as the payl
532532
by referencing the `$` path.
533533

534534
```ts
535-
new sfn.Task(this, 'Invoke with state input');
535+
import * as lambda from '@aws-cdk/aws-lambda';
536+
import * as sfn from '@aws-cdk/aws-stepfunctions';
537+
import * as tasks from '@aws-cdk/aws-stepfunctions-tasks';
538+
539+
const myLambda = new lambda.Function(this, 'my sample lambda', {
540+
code: Code.fromInline(`exports.handler = async () => {
541+
return {
542+
statusCode: '200',
543+
body: 'hello, world!'
544+
};
545+
};`),
546+
runtime: Runtime.NODEJS_12_X,
547+
handler: 'index.handler',
548+
});
549+
550+
new tasks.LambdaInvoke(this, 'Invoke with state input', {
551+
lambdaFunction: myLambda,
552+
});
536553
```
537554

538555
When a function is invoked, the Lambda service sends [these response
@@ -545,27 +562,25 @@ The following snippet invokes a Lambda Function by referencing the `$.Payload` p
545562
to reference the output of a Lambda executed before it.
546563

547564
```ts
548-
new sfn.Task(this, 'Invoke with empty object as payload', {
549-
task: new tasks.RunLambdaTask(myLambda, {
550-
payload: sfn.TaskInput.fromObject({})
551-
}),
565+
new tasks.LambdaInvoke(this, 'Invoke with empty object as payload', {
566+
lambdaFunction: myLambda,
567+
payload: sfn.TaskInput.fromObject({}),
552568
});
553569

554-
new sfn.Task(this, 'Invoke with payload field in the state input', {
555-
task: new tasks.RunLambdaTask(myOtherLambda, {
556-
payload: sfn.TaskInput.fromDataAt('$.Payload'),
557-
}),
570+
// use the output of myLambda as input
571+
new tasks.LambdaInvoke(this, 'Invoke with payload field in the state input', {
572+
lambdaFunction: myOtherLambda,
573+
payload: sfn.TaskInput.fromDataAt('$.Payload'),
558574
});
559575
```
560576

561577
The following snippet invokes a Lambda and sets the task output to only include
562578
the Lambda function response.
563579

564580
```ts
565-
new sfn.Task(this, 'Invoke and set function response as task output', {
566-
task: new tasks.RunLambdaTask(myLambda, {
567-
payload: sfn.TaskInput.fromDataAt('$'),
568-
}),
581+
new tasks.LambdaInvoke(this, 'Invoke and set function response as task output', {
582+
lambdaFunction: myLambda,
583+
payload: sfn.TaskInput.fromDataAt('$'),
569584
outputPath: '$.Payload',
570585
});
571586
```
@@ -581,15 +596,14 @@ The following snippet invokes a Lambda with the task token as part of the input
581596
to the Lambda.
582597

583598
```ts
584-
const task = new sfn.Task(stack, 'Invoke with callback', {
585-
task: new tasks.RunLambdaTask(myLambda, {
586-
integrationPattern: sfn.ServiceIntegrationPattern.WAIT_FOR_TASK_TOKEN,
587-
payload: {
588-
token: sfn.Context.taskToken,
589-
input: sfn.TaskInput.fromDataAt('$.someField'),
590-
}
591-
})
592-
});
599+
new tasks.LambdaInvoke(stack, 'Invoke with callback', {
600+
lambdaFunction: myLambda,
601+
integrationPattern: sfn.IntegrationPattern.WAIT_FOR_TASK_TOKEN,
602+
payload: sfn.TaskInput.fromObject({
603+
token: sfn.Context.taskToken,
604+
input: sfn.Data.stringAt('$.someField'),
605+
}),
606+
});
593607
```
594608

595609
⚠️ The task will pause until it receives that task token back with a `SendTaskSuccess` or `SendTaskFailure`
@@ -677,28 +691,28 @@ You can call the [`Publish`](https://docs.aws.amazon.com/sns/latest/api/API_Publ
677691

678692
```ts
679693
import * as sns from '@aws-cdk/aws-sns';
694+
import * as sfn from '@aws-cdk/aws-stepfunctions';
695+
import * as tasks from '@aws-cdk/aws-stepfunctions-tasks';
680696

681697
// ...
682698

683699
const topic = new sns.Topic(this, 'Topic');
684700

685701
// Use a field from the execution data as message.
686-
const task1 = new sfn.Task(this, 'Publish1', {
687-
task: new tasks.PublishToTopic(topic, {
688-
integrationPattern: sfn.ServiceIntegrationPattern.FIRE_AND_FORGET,
689-
message: TaskInput.fromDataAt('$.state.message'),
690-
})
702+
const task1 = new tasks.SnsPublish(this, 'Publish1', {
703+
topic,
704+
integrationPattern: sfn.IntegrationPattern.REQUEST_RESPONSE,
705+
message: sfn.TaskInput.fromDataAt('$.state.message'),
691706
});
692707

693708
// Combine a field from the execution data with
694709
// a literal object.
695-
const task2 = new sfn.Task(this, 'Publish2', {
696-
task: new tasks.PublishToTopic(topic, {
697-
message: TaskInput.fromObject({
698-
field1: 'somedata',
699-
field2: Data.stringAt('$.field2'),
700-
})
701-
})
710+
const task2 = new tasks.SnsPublish(this, 'Publish2', {
711+
topic,
712+
message: sfn.TaskInput.fromObject({
713+
field1: 'somedata',
714+
field2: sfn.Data.stringAt('$.field2'),
715+
})
702716
});
703717
```
704718

@@ -740,31 +754,27 @@ You can call the [`SendMessage`](https://docs.aws.amazon.com/AWSSimpleQueueServi
740754
to send a message to an SQS queue.
741755

742756
```ts
757+
import * as sfn from '@aws-cdk/aws-stepfunctions';
758+
import * as tasks from '@aws-cdk/aws-stepfunctions-tasks';
743759
import * as sqs from '@aws-cdk/aws-sqs';
744760

745761
// ...
746762

747-
const queue = new sns.Queue(this, 'Queue');
763+
const queue = new sqs.Queue(this, 'Queue');
748764

749765
// Use a field from the execution data as message.
750-
const task1 = new sfn.Task(this, 'Send1', {
751-
task: new tasks.SendToQueue(queue, {
752-
messageBody: TaskInput.fromDataAt('$.message'),
753-
// Only for FIFO queues
754-
messageGroupId: '1234'
755-
})
766+
const task1 = new tasks.SqsSendMessage(this, 'Send1', {
767+
queue,
768+
messageBody: sfn.TaskInput.fromDataAt('$.message'),
756769
});
757770

758771
// Combine a field from the execution data with
759772
// a literal object.
760-
const task2 = new sfn.Task(this, 'Send2', {
761-
task: new tasks.SendToQueue(queue, {
762-
messageBody: TaskInput.fromObject({
763-
field1: 'somedata',
764-
field2: Data.stringAt('$.field2'),
765-
}),
766-
// Only for FIFO queues
767-
messageGroupId: '1234'
768-
})
773+
const task2 = new tasks.SqsSendMessage(this, 'Send2', {
774+
queue,
775+
messageBody: sfn.TaskInput.fromObject({
776+
field1: 'somedata',
777+
field2: sfn.Data.stringAt('$.field2'),
778+
}),
769779
});
770780
```

packages/@aws-cdk/aws-stepfunctions-tasks/lib/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
export * from './lambda/invoke-function';
22
export * from './lambda/run-lambda-task';
3+
export * from './lambda/invoke';
34
export * from './invoke-activity';
45
export * from './ecs/run-ecs-task-base'; // Remove this once we can
56
export * from './ecs/run-ecs-task-base-types';
67
export * from './sns/publish-to-topic';
8+
export * from './sns/publish';
79
export * from './sqs/send-to-queue';
10+
export * from './sqs/send-message';
811
export * from './ecs/run-ecs-ec2-task';
912
export * from './ecs/run-ecs-fargate-task';
1013
export * from './sagemaker/sagemaker-task-base-types';

packages/@aws-cdk/aws-stepfunctions-tasks/lib/lambda/invoke-function.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import * as sfn from '@aws-cdk/aws-stepfunctions';
55
/**
66
* Properties for InvokeFunction
77
*
8-
* @deprecated use `RunLambdaTask`
8+
* @deprecated use `LambdaInvoke`
99
*/
1010
export interface InvokeFunctionProps {
1111
/**
@@ -25,7 +25,7 @@ export interface InvokeFunctionProps {
2525
*
2626
* OUTPUT: the output of this task is the return value of the Lambda Function.
2727
*
28-
* @deprecated Use `RunLambdaTask`
28+
* @deprecated Use `LambdaInvoke`
2929
*/
3030
export class InvokeFunction implements sfn.IStepFunctionsTask {
3131
constructor(private readonly lambdaFunction: lambda.IFunction, private readonly props: InvokeFunctionProps = {}) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import * as iam from '@aws-cdk/aws-iam';
2+
import * as lambda from '@aws-cdk/aws-lambda';
3+
import * as sfn from '@aws-cdk/aws-stepfunctions';
4+
import * as cdk from '@aws-cdk/core';
5+
import { integrationResourceArn, validatePatternSupported } from '../private/task-utils';
6+
7+
/**
8+
* Properties for invoking a Lambda function with LambdaInvoke
9+
*/
10+
export interface LambdaInvokeProps extends sfn.TaskStateBaseProps {
11+
12+
/**
13+
* Lambda function to invoke
14+
*/
15+
readonly lambdaFunction: lambda.IFunction;
16+
17+
/**
18+
* The JSON that will be supplied as input to the Lambda function
19+
*
20+
* @default - The state input (JSON path '$')
21+
*/
22+
readonly payload?: sfn.TaskInput;
23+
24+
/**
25+
* Invocation type of the Lambda function
26+
*
27+
* @default InvocationType.REQUEST_RESPONSE
28+
*/
29+
readonly invocationType?: LambdaInvocationType;
30+
31+
/**
32+
* Up to 3583 bytes of base64-encoded data about the invoking client
33+
* to pass to the function.
34+
*
35+
* @default - No context
36+
*/
37+
readonly clientContext?: string;
38+
39+
/**
40+
* Version or alias to invoke a published version of the function
41+
*
42+
* You only need to supply this if you want the version of the Lambda Function to depend
43+
* on data in the state machine state. If not, you can pass the appropriate Alias or Version object
44+
* directly as the `lambdaFunction` argument.
45+
*
46+
* @default - Version or alias inherent to the `lambdaFunction` object.
47+
*/
48+
readonly qualifier?: string;
49+
}
50+
51+
/**
52+
* Invoke a Lambda function as a Task
53+
*
54+
* @see https://docs.aws.amazon.com/step-functions/latest/dg/connect-lambda.html
55+
*/
56+
export class LambdaInvoke extends sfn.TaskStateBase {
57+
58+
private static readonly SUPPORTED_INTEGRATION_PATTERNS: sfn.IntegrationPattern[] = [
59+
sfn.IntegrationPattern.REQUEST_RESPONSE,
60+
sfn.IntegrationPattern.WAIT_FOR_TASK_TOKEN,
61+
];
62+
63+
protected readonly taskMetrics?: sfn.TaskMetricsConfig;
64+
protected readonly taskPolicies?: iam.PolicyStatement[];
65+
66+
private readonly integrationPattern: sfn.IntegrationPattern;
67+
68+
constructor(scope: cdk.Construct, id: string, private readonly props: LambdaInvokeProps) {
69+
super(scope, id, props);
70+
this.integrationPattern = props.integrationPattern ?? sfn.IntegrationPattern.REQUEST_RESPONSE;
71+
72+
validatePatternSupported(this.integrationPattern, LambdaInvoke.SUPPORTED_INTEGRATION_PATTERNS);
73+
74+
if (this.integrationPattern === sfn.IntegrationPattern.WAIT_FOR_TASK_TOKEN
75+
&& !sfn.FieldUtils.containsTaskToken(props.payload)) {
76+
throw new Error('Task Token is required in `payload` for callback. Use Context.taskToken to set the token.');
77+
}
78+
79+
this.taskMetrics = {
80+
metricPrefixSingular: 'LambdaFunction',
81+
metricPrefixPlural: 'LambdaFunctions',
82+
metricDimensions: {
83+
LambdaFunctionArn: this.props.lambdaFunction.functionArn,
84+
...(this.props.qualifier && { Qualifier: this.props.qualifier }),
85+
},
86+
};
87+
88+
this.taskPolicies = [
89+
new iam.PolicyStatement({
90+
resources: [this.props.lambdaFunction.functionArn],
91+
actions: ['lambda:InvokeFunction'],
92+
}),
93+
];
94+
}
95+
96+
/**
97+
* Provides the Lambda Invoke service integration task configuration
98+
*/
99+
protected renderTask(): any {
100+
return {
101+
Resource: integrationResourceArn('lambda', 'invoke', this.integrationPattern),
102+
Parameters: sfn.FieldUtils.renderObject({
103+
FunctionName: this.props.lambdaFunction.functionArn,
104+
Payload: this.props.payload ? this.props.payload.value : sfn.TaskInput.fromDataAt('$').value,
105+
InvocationType: this.props.invocationType,
106+
ClientContext: this.props.clientContext,
107+
Qualifier: this.props.qualifier,
108+
}),
109+
};
110+
}
111+
}
112+
113+
/**
114+
* Invocation type of a Lambda
115+
*/
116+
export enum LambdaInvocationType {
117+
/**
118+
* Invoke the function synchronously.
119+
*
120+
* Keep the connection open until the function returns a response or times out.
121+
* The API response includes the function response and additional data.
122+
*/
123+
REQUEST_RESPONSE = 'RequestResponse',
124+
125+
/**
126+
* Invoke the function asynchronously.
127+
*
128+
* Send events that fail multiple times to the function's dead-letter queue (if it's configured).
129+
* The API response only includes a status code.
130+
*/
131+
EVENT = 'Event',
132+
133+
/**
134+
* Validate parameter values and verify that the user or role has permission to invoke the function.
135+
*/
136+
DRY_RUN = 'DryRun'
137+
}

packages/@aws-cdk/aws-stepfunctions-tasks/lib/lambda/run-lambda-task.ts

+3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { getResourceArn } from '../resource-arn-suffix';
55

66
/**
77
* Properties for RunLambdaTask
8+
*
9+
* @deprecated Use `LambdaInvoke`
810
*/
911
export interface RunLambdaTaskProps {
1012
/**
@@ -58,6 +60,7 @@ export interface RunLambdaTaskProps {
5860
* `SendTaskSuccess/SendTaskFailure` in `waitForTaskToken` mode.
5961
*
6062
* @see https://docs.aws.amazon.com/step-functions/latest/dg/connect-lambda.html
63+
* @deprecated Use `LambdaInvoke`
6164
*/
6265
export class RunLambdaTask implements sfn.IStepFunctionsTask {
6366
private readonly integrationPattern: sfn.ServiceIntegrationPattern;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import {
2+
IntegrationPattern,
3+
} from '@aws-cdk/aws-stepfunctions';
4+
import { Aws } from '@aws-cdk/core';
5+
6+
/**
7+
* Verifies that a validation pattern is supported for a service integration
8+
*
9+
*/
10+
export function validatePatternSupported(integrationPattern: IntegrationPattern, supportedPatterns: IntegrationPattern[]) {
11+
if (!supportedPatterns.includes(integrationPattern)) {
12+
throw new Error(`Unsupported service integration pattern. Supported Patterns: ${supportedPatterns}. Received: ${integrationPattern}`);
13+
}
14+
}
15+
16+
/**
17+
* Suffixes corresponding to different service integration patterns
18+
*
19+
* Key is the service integration pattern, value is the resource ARN suffix.
20+
*
21+
* @see https://docs.aws.amazon.com/step-functions/latest/dg/connect-to-resource.html
22+
*/
23+
const resourceArnSuffix: Record<IntegrationPattern, string> = {
24+
[IntegrationPattern.REQUEST_RESPONSE]: '',
25+
[IntegrationPattern.RUN_JOB]: '.sync',
26+
[IntegrationPattern.WAIT_FOR_TASK_TOKEN]: '.waitForTaskToken',
27+
};
28+
29+
export function integrationResourceArn(service: string, api: string, integrationPattern: IntegrationPattern): string {
30+
if (!service || !api) {
31+
throw new Error("Both 'service' and 'api' must be provided to build the resource ARN.");
32+
}
33+
return `arn:${Aws.PARTITION}:states:::${service}:${api}` +
34+
(integrationPattern ? resourceArnSuffix[integrationPattern] : '');
35+
}

0 commit comments

Comments
 (0)