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(apigateway): step functions integration #16827

Merged
merged 38 commits into from
Nov 25, 2021
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
014e130
StepFunctionsRestApi implemented along with unit and integration test…
Oct 6, 2021
23b6fd7
Fixing request Context string build and template string vtl.
Nov 9, 2021
819468f
Pull rebase
Nov 9, 2021
128a4fa
Fixing request Context string build and template string vtl.
Nov 9, 2021
a28fdab
Improving structure of the code as per PR comments.
Nov 11, 2021
b4c5b53
Added more integration tests. Created a VTL template. Added more para…
saqibdhuka Nov 20, 2021
f8f332f
Merge branch 'master' into StepFunctionsRestApi-branch-issue#15081
diegotry Nov 23, 2021
50680d5
Update readme, removed cors check, fixed integrations
diegotry Nov 23, 2021
a6b8165
Update test signature - changed interface
diegotry Nov 23, 2021
10e361f
update tests to include querystring and path as default
diegotry Nov 23, 2021
151ead7
Remove deprecated Step Functions Task utilization
diegotry Nov 23, 2021
7e416ef
update fixtures to include step functions and updated examples
diegotry Nov 23, 2021
ab86c7e
Update packages/@aws-cdk/aws-apigateway/README.md
diegotry Nov 23, 2021
5306f22
Update packages/@aws-cdk/aws-apigateway/README.md
diegotry Nov 23, 2021
16ef951
Update comments/docs to fix Rosetta
diegotry Nov 23, 2021
d0fe918
Update SFN examples for Rosetta
diegotry Nov 23, 2021
fff8835
Merge branch 'master' into StepFunctionsRestApi-branch-issue#15081
diegotry Nov 24, 2021
663ece7
Refactor requestContext dynamic options and update documentation
diegotry Nov 24, 2021
3bf2c8b
change the integration class api
Nov 24, 2021
7d950b1
Doc update
diegotry Nov 24, 2021
2f10576
Doc update
diegotry Nov 24, 2021
dc3b54f
rename interface
Nov 24, 2021
8ebf0c7
Doc update
diegotry Nov 24, 2021
de43130
Doc update
diegotry Nov 24, 2021
2cc14dc
Update packages/@aws-cdk/aws-apigateway/test/integrations/stepfunctio…
diegotry Nov 24, 2021
d2ad11d
fix snippet
Nov 24, 2021
1ca20ea
Revert "rename interface"
Nov 24, 2021
e4b9ff5
Update fields documentation (requestContext, querystring, path and he…
diegotry Nov 24, 2021
ede456f
Update code sample to use path as an object instead of strung
diegotry Nov 24, 2021
c8b1630
Move functions to the end of the file
diegotry Nov 24, 2021
8aaf67a
Include tests for request context fields inside the StepFunctionsExec…
diegotry Nov 25, 2021
e501659
Update packages/@aws-cdk/aws-apigateway/test/integ.stepfunctions-api.ts
diegotry Nov 25, 2021
9a95f46
Update integ test.exepcted.json
diegotry Nov 25, 2021
884397e
update tests
Nov 25, 2021
0bf4637
update snippets
Nov 25, 2021
2623b65
code tweaks
Nov 25, 2021
57763b8
Remove stringLike fix as it is merged from the other PR
diegotry Nov 25, 2021
0ff219a
Merge branch 'master' into StepFunctionsRestApi-branch-issue#15081
diegotry Nov 25, 2021
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 @@ -243,7 +243,9 @@ function isCallable(x: any): x is ((...args: any[]) => any) {
*/
export function stringLike(pattern: string): PropertyMatcher {
// Replace * with .* in the string, escape the rest and brace with ^...$
const regex = new RegExp(`^${pattern.split('*').map(escapeRegex).join('.*')}$`);

// needs rebase from https://github.com/aws/aws-cdk/pull/17692/files
const regex = new RegExp(`^${pattern.split('*').map(escapeRegex).join('.*')}$`, 'm');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amazing! I've approved the PR and it is now merged.


return annotateMatcher({ $stringContaining: pattern }, (value: any, failure: InspectionFailure) => {
if (typeof value !== 'string') {
Expand Down
123 changes: 123 additions & 0 deletions packages/@aws-cdk/aws-apigateway/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ running on AWS Lambda, or any web application.
- [Defining APIs](#defining-apis)
- [Breaking up Methods and Resources across Stacks](#breaking-up-methods-and-resources-across-stacks)
- [AWS Lambda-backed APIs](#aws-lambda-backed-apis)
- [AWS StepFunctions backed APIs](#aws-stepfunctions-backed-APIs)
- [Integration Targets](#integration-targets)
- [Usage Plan & API Keys](#usage-plan--api-keys)
- [Working with models](#working-with-models)
Expand Down Expand Up @@ -106,6 +107,128 @@ item.addMethod('GET'); // GET /items/{item}
item.addMethod('DELETE', new apigateway.HttpIntegration('http://amazon.com'));
```

## AWS StepFunctions backed APIs

You can use Amazon API Gateway with AWS Step Functions as the backend integration, specifically Synchronous Express Workflows.

The `StepFunctionsRestApi` only supports integration with Synchronous Express state machine. The `StepFunctionsRestApi` construct makes this easy by setting up input, output and error mapping.

The construct sets up an API endpoint and maps the `ANY` HTTP method and any calls to the API endpoint starts an express workflow execution for the underlying state machine.

Invoking the endpoint with any HTTP method (`GET`, `POST`, `PUT`, `DELETE`, ...) in the example below will send the request to the state machine as a new execution. On success, an HTTP code `200` is returned with the execution output as the Response Body.

If the execution fails, an HTTP `500` response is returned with the `error` and `cause` from the execution output as the Response Body. If the request is invalid (ex. bad execution input) HTTP code `400` is returned.

The response from the invocation contains only the `output` field from the
[StartSyncExecution](https://docs.aws.amazon.com/step-functions/latest/apireference/API_StartSyncExecution.html#API_StartSyncExecution_ResponseSyntax) API.
In case of failures, the fields `error` and `cause` are returned as part of the response.
Other metadata such as billing details, AWS account ID and resource ARNs are not returned in the API response.

By default, a `prod` stage is provisioned.

In order to reduce the payload size sent to AWS Step Functions, `headers` are not forwarded to the Step Functions execution input. It is possible to choose whether `headers`, `requestContext`, `path` and `querystring` are included or not. By default, `headers` are excluded in all requests.

More details about AWS Step Functions payload limit can be found at https://docs.aws.amazon.com/step-functions/latest/dg/limits-overview.html#service-limits-task-executions.

The following code defines a REST API that routes all requests to the specified AWS StepFunctions state machine:

```ts
const stateMachineDefinition = new stepfunctions.Pass(this, 'PassState');

const stateMachine: stepfunctions.IStateMachine = new stepfunctions.StateMachine(this, 'StateMachine', {
definition: stateMachineDefinition,
stateMachineType: stepfunctions.StateMachineType.EXPRESS,
});

new apigateway.StepFunctionsRestApi(this, 'StepFunctionsRestApi', {
deploy: true,
stateMachine: stateMachine,
});
```

When the REST API endpoint configuration above is invoked using POST, as follows -

```bash
curl -X POST -d '{ "customerId": 1 }' https://example.com/
```

AWS Step Functions will receive the request body in its input as follows:

```json
{
"body": {
"customerId": 1
},
"path": "/",
"querystring": {}
}
```

When the endpoint is invoked at path '/users/5' using the HTTP GET method as below:

```bash
curl -X GET https://example.com/users/5?foo=bar
```

AWS Step Functions will receive the following execution input:

```json
{
"body": {},
"path": {
"users": "5"
},
"querystring": {
"foo": "bar"
}
}
```

Additional information around the request such as the request context and headers can be included as part of the input
forwarded to the state machine. The following example enables headers to be included in the input but not query string.

```ts
const stateMachineDefinition = new stepfunctions.Pass(this, 'PassState');

const stateMachine: stepfunctions.IStateMachine = new stepfunctions.StateMachine(this, 'StateMachine', {
definition: stateMachineDefinition,
stateMachineType: stepfunctions.StateMachineType.EXPRESS,
});

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(minor) no need to repeat this again.

Just focus on the code that is changing.

Suggested change
const stateMachineDefinition = new stepfunctions.Pass(this, 'PassState');
const stateMachine: stepfunctions.IStateMachine = new stepfunctions.StateMachine(this, 'StateMachine', {
definition: stateMachineDefinition,
stateMachineType: stepfunctions.StateMachineType.EXPRESS,
});

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've pushed a commit that fixes this up.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

new apigateway.StepFunctionsRestApi(this, 'StepFunctionsRestApi', {
stateMachine: stateMachine,
headers: true,
path: false,
querystring: false,
requestContext: {
caller: true,
user: true,
},
});
```

In such a case, when the endpoint is invoked as below:

```bash
curl -X GET https://example.com/
```

AWS Step Functions will receive the following execution input:

```json
{
"headers": {
"Accept": "...",
"CloudFront-Forwarded-Proto": "...",
},
"requestContext": {
"accountId": "...",
"apiKey": "...",
},
"body": {}
}
```

### Breaking up Methods and Resources across Stacks

It is fairly common for REST APIs with a large number of Resources and Methods to hit the [CloudFormation
Expand Down
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-apigateway/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export * from './authorizers';
export * from './access-log';
export * from './api-definition';
export * from './gateway-response';
export * from './stepfunctions-api';

// AWS::ApiGateway CloudFormation Resources:
export * from './apigateway.generated';
Expand Down
2 changes: 2 additions & 0 deletions packages/@aws-cdk/aws-apigateway/lib/integrations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ export * from './aws';
export * from './lambda';
export * from './http';
export * from './mock';
export * from './stepfunctions';
export * from './request-context';
161 changes: 161 additions & 0 deletions packages/@aws-cdk/aws-apigateway/lib/integrations/request-context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/**
* Configure what must be included in the `requestContext`
*
* More details can be found at mapping templates documentation.
* @see https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html
*/
export interface RequestContext {
/**
* Represents the information of $context.identity.accountId
*
* Whether the AWS account of the API owner should be included in the request context
* @default false
*/
readonly accountId?: boolean;

/**
* Represents the information of $context.apiId
*
* Whether the identifier API Gateway assigns to your API should be included in the request context.
* @default false
*/
readonly apiId?: boolean;

/**
* Represents the information of $context.identity.apiKey
*
* Whether the API key associated with the request should be included in request context.
* @default false
*/
readonly apiKey?: boolean;

/**
* Represents the information of $context.authorizer.principalId
*
* Whether the principal user identifier associated with the token sent by the client and returned
* from an API Gateway Lambda authorizer should be included in the request context.
* @default false
*/
readonly authorizerPrincipalId?: boolean;

/**
* Represents the information of $context.identity.caller
*
* Whether the principal identifier of the caller that signed the request should be included in the request context.
* Supported for resources that use IAM authorization.
* @default false
*/
readonly caller?: boolean;

/**
* Represents the information of $context.identity.cognitoAuthenticationProvider
*
* Whether the list of the Amazon Cognito authentication providers used by the caller making the request should be included in the request context.
* Available only if the request was signed with Amazon Cognito credentials.
* @default false
*/
readonly cognitoAuthenticationProvider?: boolean;

/**
* Represents the information of $context.identity.cognitoAuthenticationType
*
* Whether the Amazon Cognito authentication type of the caller making the request should be included in the request context.
* Available only if the request was signed with Amazon Cognito credentials.
* Possible values include authenticated for authenticated identities and unauthenticated for unauthenticated identities.
* @default false
*/
readonly cognitoAuthenticationType?: boolean;

/**
* Represents the information of $context.identity.cognitoIdentityId
*
* Whether the Amazon Cognito identity ID of the caller making the request should be included in the request context.
* Available only if the request was signed with Amazon Cognito credentials.
* @default false
*/
readonly cognitoIdentityId?: boolean;

/**
* Represents the information of $context.identity.cognitoIdentityPoolId
*
* Whether the Amazon Cognito identity pool ID of the caller making the request should be included in the request context.
* Available only if the request was signed with Amazon Cognito credentials.
* @default false
*/
readonly cognitoIdentityPoolId?: boolean;

/**
* Represents the information of $context.httpMethod
*
* Whether the HTTP method used should be included in the request context.
* Valid values include: DELETE, GET, HEAD, OPTIONS, PATCH, POST, and PUT.
* @default false
*/
readonly httpMethod?: boolean;

/**
* Represents the information of $context.stage
*
* Whether the deployment stage of the API request should be included in the request context.
* @default false
*/
readonly stage?: boolean;

/**
* Represents the information of $context.identity.sourceIp
*
* Whether the source IP address of the immediate TCP connection making the request
* to API Gateway endpoint should be included in the request context.
* @default false
*/
readonly sourceIp?: boolean;

/**
* Represents the information of $context.identity.user
*
* Whether the principal identifier of the user that will be authorized should be included in the request context.
* Supported for resources that use IAM authorization.
* @default false
*/
readonly user?: boolean;

/**
* Represents the information of $context.identity.userAgent
*
* Whether the User-Agent header of the API caller should be included in the request context.
* @default false
*/
readonly userAgent?: boolean;

/**
* Represents the information of $context.identity.userArn
*
* Whether the Amazon Resource Name (ARN) of the effective user identified after authentication should be included in the request context.
* @default false
*/
readonly userArn?: boolean;

/**
* Represents the information of $context.requestId
*
* Whether the ID for the request should be included in the request context.
* @default false
*/
readonly requestId?: boolean;

/**
* Represents the information of $context.resourceId
*
* Whether the identifier that API Gateway assigns to your resource should be included in the request context.
* @default false
*/
readonly resourceId?: boolean;

/**
* Represents the information of $context.resourcePath
*
* Whether the path to the resource should be included in the request context.
* @default false
*/
readonly resourcePath?: boolean;
}
Loading