-
Notifications
You must be signed in to change notification settings - Fork 4k
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-tasks): support for APIGW API: Invoke #11565
Changes from 7 commits
51e47d8
fbb40d6
e14080a
32d0e26
494334b
ac29551
c47bb7e
9cd40c7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,6 +26,8 @@ This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aw | |
- [ResultPath](#resultpath) | ||
- [Parameters](#task-parameters-from-the-state-json) | ||
- [Evaluate Expression](#evaluate-expression) | ||
- [API Gateway](#api-gateway) | ||
- [Invoke](#invoke) | ||
- [Athena](#athena) | ||
- [StartQueryExecution](#startQueryExecution) | ||
- [GetQueryExecution](#getQueryExecution) | ||
|
@@ -211,6 +213,28 @@ runtime to use to evaluate the expression. Currently, the only runtime | |
supported is `lambda.Runtime.NODEJS_10_X`. | ||
|
||
|
||
## API Gateway | ||
|
||
Step Functions supports [API Gateway](https://docs.aws.amazon.com/step-functions/latest/dg/connect-api-gateway.html) through the service integration pattern. | ||
|
||
### Invoke | ||
|
||
The [Invoke](https://docs.aws.amazon.com/step-functions/latest/dg/connect-api-gateway.html) API calls the API endpoint. | ||
|
||
```ts | ||
import * as sfn from '@aws-cdk/aws-stepfunctions'; | ||
import * as tasks from `@aws-cdk/aws-stepfunctions-tasks`; | ||
|
||
const restApi = new apigateway.RestApi(stack, 'MyRestApi'); | ||
|
||
const invokeJob = new tasks.ApiGatewayInvoke(stack, 'Invoke APIGW', { | ||
api: restApi, | ||
apiEndpoint: restApi.restApiId, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why does this need to be an input parameter as well? you're already accepting the |
||
stageName: 'prod', | ||
method: ApiGatewayMethodType.GET, | ||
}); | ||
``` | ||
|
||
## Athena | ||
|
||
Step Functions supports [Athena](https://docs.aws.amazon.com/step-functions/latest/dg/connect-athena.html) through the service integration pattern. | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,218 @@ | ||||||
import * as apigateway from '@aws-cdk/aws-apigateway'; | ||||||
import * as iam from '@aws-cdk/aws-iam'; | ||||||
import * as sfn from '@aws-cdk/aws-stepfunctions'; | ||||||
import * as cdk from '@aws-cdk/core'; | ||||||
import { Construct } from 'constructs'; | ||||||
import { integrationResourceArn, validatePatternSupported } from '../private/task-utils'; | ||||||
|
||||||
/** | ||||||
* Properties for invoking an API Endpoint with ApiGatewayInvoke | ||||||
*/ | ||||||
export interface ApiGatewayInvokeProps extends sfn.TaskStateBaseProps { | ||||||
|
||||||
/** API to call */ | ||||||
readonly api: apigateway.IRestApi; | ||||||
|
||||||
/** | ||||||
* hostname of an API Gateway URL | ||||||
* @example {ApiId}.execute-api.{region}.amazonaws.com | ||||||
*/ | ||||||
readonly apiEndpoint: string; | ||||||
Sumeet-Badyal marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this should not be needed anymore. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the endpoint can be inferred from the |
||||||
|
||||||
/** Http method for the API */ | ||||||
readonly method: HttpMethod; | ||||||
|
||||||
/** | ||||||
* HTTP request information that does not relate to contents of the request | ||||||
* @default - No headers | ||||||
* @example | ||||||
* Headers: { | ||||||
* type: 1, | ||||||
* value:{ | ||||||
* 'TaskToken.$': 'States.Array($$.Task.Token)', | ||||||
* } | ||||||
* }, | ||||||
*/ | ||||||
readonly headers?: { [key: string]: any }; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. confirming to ensure we have the right type here: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Anyone digging into this? Happy to try and help; looking forward to putting this functionality to use ;) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @eikeon - happy to have your help here!! :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this pretty much the sole remaining thing holding up this pull request? Can save some time to attempt to dig into this headers issue later today There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. headers here probably should be similar to input here: packages/@aws-cdk/aws-stepfunctions-tasks/test/start-execution.test.ts and packages/@aws-cdk/aws-stepfunctions-tasks/lib/start-execution.ts -- and they both have the same type. Gotta run for a few hours, but will trace how headers is being used in this pull request compared to other working examples. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. readonly headers?: { [key: string]: string[] } There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I also see a string value in one of the examples on that doc page: readonly headers?: { [key: string]: string[] | string } |
||||||
|
||||||
/** | ||||||
* Name of the stage where the API is deployed to in API Gateway | ||||||
* @default - Required for REST and $default for HTTP | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: be sure to describe what |
||||||
*/ | ||||||
readonly stageName?: string; | ||||||
|
||||||
/** | ||||||
* Path parameters appended after API endpoint | ||||||
* @default - No path | ||||||
*/ | ||||||
readonly path?: string; | ||||||
|
||||||
/** | ||||||
* Query strings attatched to end of request | ||||||
* @default - No query parameters | ||||||
* @example | ||||||
* "QueryParameters": { | ||||||
* "billId": ["123", "456"] | ||||||
* }, | ||||||
*/ | ||||||
readonly queryParameters?: { [key: string]: any }; | ||||||
|
||||||
/** | ||||||
* HTTP Request body | ||||||
* @default - No requestBody | ||||||
* @example | ||||||
* "RequestBody": { | ||||||
* "billId": ["my-new-bill"] | ||||||
* }, | ||||||
*/ | ||||||
readonly requestBody?: sfn.TaskInput; | ||||||
|
||||||
/** | ||||||
* Authentication methods | ||||||
* @default - NO_AUTH | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
*/ | ||||||
readonly authType?: AuthType; | ||||||
|
||||||
} | ||||||
|
||||||
/** | ||||||
* Invoke an API endpoint as a Task | ||||||
* | ||||||
* @see https://docs.aws.amazon.com/step-functions/latest/dg/connect-api-gateway.html | ||||||
*/ | ||||||
export class ApiGatewayInvoke extends sfn.TaskStateBase { | ||||||
|
||||||
private static readonly SUPPORTED_INTEGRATION_PATTERNS: sfn.IntegrationPattern[] = [ | ||||||
sfn.IntegrationPattern.REQUEST_RESPONSE, | ||||||
sfn.IntegrationPattern.WAIT_FOR_TASK_TOKEN, | ||||||
]; | ||||||
protected readonly taskMetrics?: sfn.TaskMetricsConfig; | ||||||
protected readonly taskPolicies?: iam.PolicyStatement[]; | ||||||
protected readonly apiEndpoint: string; | ||||||
|
||||||
private readonly integrationPattern: sfn.IntegrationPattern; | ||||||
|
||||||
constructor(scope: Construct, id: string, private readonly props: ApiGatewayInvokeProps) { | ||||||
super(scope, id, props); | ||||||
this.integrationPattern = props.integrationPattern ?? sfn.IntegrationPattern.REQUEST_RESPONSE; | ||||||
|
||||||
validatePatternSupported(this.integrationPattern, ApiGatewayInvoke.SUPPORTED_INTEGRATION_PATTERNS); | ||||||
|
||||||
this.taskPolicies = this.createPolicyStatements(); | ||||||
this.apiEndpoint = this.createApiEndpoint(); | ||||||
} | ||||||
|
||||||
/** | ||||||
* Provides the API Gateway Invoke service integration task configuration | ||||||
*/ | ||||||
/** | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
* @internal | ||||||
*/ | ||||||
protected _renderTask(): any { | ||||||
return { | ||||||
Resource: integrationResourceArn('apigateway', 'invoke', this.integrationPattern), | ||||||
Parameters: sfn.FieldUtils.renderObject({ | ||||||
ApiEndpoint: this.apiEndpoint, | ||||||
Method: this.props.method, | ||||||
Headers: this.props.headers, | ||||||
Stage: this.props.stageName, | ||||||
Path: this.props.path, | ||||||
QueryParameters: this.props.queryParameters, | ||||||
RequestBody: this.props.requestBody, | ||||||
AuthType: this.props.authType ? this.props.authType : 'NO_AUTH', | ||||||
}), | ||||||
}; | ||||||
} | ||||||
|
||||||
/** | ||||||
* Gets the "execute-api" ARN | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: I think the doc string should be a little more informative than the method name. Perhaps describe what that ARN is / what it's used for, show an example of what it looks like, etc. |
||||||
* @returns The "execute-api" ARN. | ||||||
* @default "*" returns the execute API ARN for all methods/resources in | ||||||
* this API. | ||||||
* @param method The method (default `*`) | ||||||
* @param path The resource path. Must start with '/' (default `*`) | ||||||
* @param stage The stage (default `*`) | ||||||
*/ | ||||||
get arnForExecuteApi() { | ||||||
return this.props.api.arnForExecuteApi(this.props.method, this.props.path, this.props.stageName); | ||||||
} | ||||||
|
||||||
/** | ||||||
* Generates the api endpoint | ||||||
* @returns The api id | ||||||
* @example {ApiId}.execute-api.{region}.amazonaws.com | ||||||
*/ | ||||||
private createApiEndpoint(): string { | ||||||
const apiStack = cdk.Stack.of(this.props.api); | ||||||
return `${this.props.api.restApiId}.execute-api.${apiStack.region}.${apiStack.urlSuffix}`; | ||||||
} | ||||||
|
||||||
/** | ||||||
* This generates the PolicyStatements required by the Task to call invoke. | ||||||
*/ | ||||||
private createPolicyStatements(): iam.PolicyStatement[] { | ||||||
if (this.props.authType === AuthType.IAM_ROLE) { | ||||||
return [ | ||||||
new iam.PolicyStatement({ | ||||||
resources: [this.arnForExecuteApi], | ||||||
actions: ['ExecuteAPI:Invoke'], | ||||||
}), | ||||||
]; | ||||||
} else if (this.props.authType === AuthType.RESOURCE_POLICY) { | ||||||
if (!sfn.FieldUtils.containsTaskToken(this.props.headers)) { | ||||||
throw new Error('Task Token is required in `headers` Use JsonPath.taskToken to set the token.'); | ||||||
} | ||||||
return [ | ||||||
new iam.PolicyStatement({ | ||||||
resources: [this.arnForExecuteApi], | ||||||
actions: ['ExecuteAPI:Invoke'], | ||||||
conditions: { | ||||||
StringEquals: { | ||||||
'aws:SourceArn': '*', | ||||||
}, | ||||||
}, | ||||||
}), | ||||||
]; | ||||||
} | ||||||
return []; | ||||||
} | ||||||
} | ||||||
|
||||||
/** Http Methods that API Gateway supports */ | ||||||
export enum HttpMethod { | ||||||
Sumeet-Badyal marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
/** Retreive data from a server at the specified resource */ | ||||||
GET = 'GET', | ||||||
|
||||||
/** Send data to the API endpoint to create or udpate a resource */ | ||||||
POST = 'POST', | ||||||
|
||||||
/** Send data to the API endpoint to update or create a resource */ | ||||||
PUT = 'PUT', | ||||||
|
||||||
/** Delete the resource at the specified endpoint */ | ||||||
DELETE = 'DELETE', | ||||||
|
||||||
/** Apply partial modifications to the resource */ | ||||||
PATCH = 'PATCH', | ||||||
|
||||||
/** Retreive data from a server at the specified resource without the response body */ | ||||||
HEAD = 'HEAD', | ||||||
|
||||||
/** Return data describing what other methods and operations the server supports */ | ||||||
OPTIONS = 'OPTIONS' | ||||||
} | ||||||
|
||||||
/** | ||||||
* The authentication method used to call the endpoint | ||||||
* @default NO_AUTH | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. enums don't have defaults, it's only where they're used that would
Suggested change
|
||||||
*/ | ||||||
export enum AuthType { | ||||||
/** Call the API direclty with no authorization method */ | ||||||
NO_AUTH = 'NO_AUTH', | ||||||
|
||||||
/** * Use the IAM role associated with the current state machine for authorization */ | ||||||
IAM_ROLE = 'IAM_ROLE', | ||||||
|
||||||
/** Use the resource policy of the API for authorization */ | ||||||
RESOURCE_POLICY = 'RESOURCE_POLICY', | ||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just confirming: this is not the same as
apigatewayv2
correct?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not the same.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But this works to invoke functions defined via apigatewayv2, correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
haven't played with it myself yet, can you clarify @Sumeet-Badyal
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From this page - https://docs.aws.amazon.com/step-functions/latest/dg/connect-api-gateway.html
This task supports invoking Rest APIs (v1) and HTTP APIs (v2) but not websocket APIs (v2).
Given our modeling of higher level constructs, we'll need to implement this as two tasks (not necessarily in the same PR). One that takes
IRestApi
from the@aws-cdk/aws-apigateway
module and another that takesIHttpApi
from the@aws-cdk/aws-apigatewayv2
module.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah; looks like someone still needs to add IHttpApi too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Sumeet-Badyal are you still working on this? Any plans to add the second task for the IHttpApi case? Else when my aws-cdk dev container finishes spinning up I could have a go -- imagine there'd be common code between the two we'd want to abstract into a shared place in the end