-
Notifications
You must be signed in to change notification settings - Fork 4k
/
Copy pathcall-aws-service.ts
168 lines (149 loc) · 5.73 KB
/
call-aws-service.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
import { Construct } from 'constructs';
import * as iam from '../../../aws-iam';
import * as sfn from '../../../aws-stepfunctions';
import { Token } from '../../../core';
import { integrationResourceArn } from '../private/task-utils';
interface CallAwsServiceOptions {
/**
* The AWS service to call.
*
* @see https://docs.aws.amazon.com/step-functions/latest/dg/supported-services-awssdk.html
*/
readonly service: string;
/**
* The API action to call.
*
* Use camelCase.
*/
readonly action: string;
/**
* Parameters for the API action call.
*
* Use PascalCase for the parameter names.
*
* @default - no parameters
*/
readonly parameters?: { [key: string]: any };
/**
* The resources for the IAM statement that will be added to the state
* machine role's policy to allow the state machine to make the API call.
*
* By default the action for this IAM statement will be `service:action`.
*/
readonly iamResources: string[];
/**
* The action for the IAM statement that will be added to the state
* machine role's policy to allow the state machine to make the API call.
*
* Use in the case where the IAM action name does not match with the
* API service/action name, e.g. `s3:ListBuckets` requires `s3:ListAllMyBuckets`.
*
* @default - service:action
*/
readonly iamAction?: string;
/**
* Additional IAM statements that will be added to the state machine
* role's policy.
*
* Use in the case where the call requires more than a single statement to
* be executed, e.g. `rekognition:detectLabels` requires also S3 permissions
* to read the object on which it must act.
*
* @default - no additional statements are added
*/
readonly additionalIamStatements?: iam.PolicyStatement[];
}
/**
* Properties for calling an AWS service's API action using JSONPath from your
* state machine.
*
* @see https://docs.aws.amazon.com/step-functions/latest/dg/supported-services-awssdk.html
*/
export interface CallAwsServiceJsonPathProps extends sfn.TaskStateJsonPathBaseProps, CallAwsServiceOptions {}
/**
* Properties for calling an AWS service's API action using JSONata from your
* state machine.
*
* @see https://docs.aws.amazon.com/step-functions/latest/dg/supported-services-awssdk.html
*/
export interface CallAwsServiceJsonataProps extends sfn.TaskStateJsonataBaseProps, CallAwsServiceOptions {}
/**
* Properties for calling an AWS service's API action from your
* state machine.
*
* @see https://docs.aws.amazon.com/step-functions/latest/dg/supported-services-awssdk.html
*/
export interface CallAwsServiceProps extends sfn.TaskStateBaseProps, CallAwsServiceOptions {}
/**
* A StepFunctions task to call an AWS service API
*/
export class CallAwsService extends sfn.TaskStateBase {
/**
* A StepFunctions task using JSONPath to call an AWS service API
*/
public static jsonPath(scope: Construct, id: string, props: CallAwsServiceJsonPathProps) {
return new CallAwsService(scope, id, props);
}
/**
* A StepFunctions task using JSONata to call an AWS service API
*/
public static jsonata(scope: Construct, id: string, props: CallAwsServiceJsonataProps) {
return new CallAwsService(scope, id, { ...props, queryLanguage: sfn.QueryLanguage.JSONATA });
}
protected readonly taskMetrics?: sfn.TaskMetricsConfig;
protected readonly taskPolicies?: iam.PolicyStatement[];
constructor(scope: Construct, id: string, private readonly props: CallAwsServiceProps) {
super(scope, id, props);
if (this.props.integrationPattern === sfn.IntegrationPattern.RUN_JOB) {
throw new Error('The RUN_JOB integration pattern is not supported for CallAwsService');
}
if (!Token.isUnresolved(this.props.action) && !this.props.action.startsWith(this.props.action[0]?.toLowerCase())) {
throw new Error(`action must be camelCase, got: ${this.props.action}`);
}
if (this.props.parameters) {
const invalidKeys = Object.keys(this.props.parameters).filter(key => !key.startsWith(key[0]?.toUpperCase()));
if (invalidKeys.length) {
throw new Error(`parameter names must be PascalCase, got: ${invalidKeys.join(', ')}`);
}
}
const iamServiceMap: Record<string, string> = {
cloudwatchlogs: 'logs',
efs: 'elasticfilesystem',
elasticloadbalancingv2: 'elasticloadbalancing',
mediapackagevod: 'mediapackage-vod',
mwaa: 'airflow',
sfn: 'states',
};
const iamService = iamServiceMap[props.service] ?? props.service;
this.taskPolicies = [
new iam.PolicyStatement({
resources: props.iamResources,
// The prefix and the action name are case insensitive
// https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_action.html
actions: [props.iamAction ?? `${iamService}:${props.action}`],
}),
...props.additionalIamStatements ?? [],
];
}
/**
* @internal
*/
protected _renderTask(topLevelQueryLanguage?: sfn.QueryLanguage): any {
const queryLanguage = sfn._getActualQueryLanguage(topLevelQueryLanguage, this.props.queryLanguage);
let service = this.props.service;
if (!Token.isUnresolved(service)) {
service = service.toLowerCase();
}
// The pattern here is: "arn:aws:states:::aws-sdk:serviceName:apiAction.[serviceIntegrationPattern]"
// See here: https://docs.aws.amazon.com/step-functions/latest/dg/supported-services-awssdk.html
// This does not change with sdk upgrades, TT:P125388388
return {
Resource: integrationResourceArn(
'aws-sdk',
`${service}:${this.props.action}`,
this.props.integrationPattern,
),
...this._renderParametersOrArguments(this.props.parameters ?? {}, queryLanguage), // Parameters is required for aws-sdk
};
}
}