Skip to content

Commit

Permalink
feat(apigatewayv2): add aws service integration -- step functions
Browse files Browse the repository at this point in the history
  • Loading branch information
zxkane committed May 3, 2021
1 parent e11d537 commit 9697e9b
Show file tree
Hide file tree
Showing 11 changed files with 633 additions and 3 deletions.
29 changes: 29 additions & 0 deletions packages/@aws-cdk/aws-apigatewayv2-integrations/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
- [HTTP APIs](#http-apis)
- [Lambda Integration](#lambda)
- [HTTP Proxy Integration](#http-proxy)
- [AWS Service Integration](#aws-service)
- [Private Integration](#private-integration)
- [WebSocket APIs](#websocket-apis)
- [Lambda WebSocket Integration](#lambda-websocket-integration)
Expand Down Expand Up @@ -78,6 +79,34 @@ httpApi.addRoutes({
});
```

### AWS Service

You can integrate your HTTP API with AWS services by using first-class integrations. A first-class integration connects
an HTTP API route to an AWS service API. When a client invokes a route that's backed by a first-class integration,
API Gateway invokes an AWS service API for you. More information can be found at [Working with AWS service integrations for HTTP APIs]
(https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-aws-services.html).

The following code configures a route `POST /start` to start execution of Step Functions state machine.

```ts
const httpApi = new HttpApi(stack, 'HttpApi');

const state = new StateMachine(stack, 'MyStateMachine', {
definition: Chain.start(new Pass(stack, 'Pass')),
stateMachineType: StateMachineType.STANDARD,
});

httpApi.addRoutes({
path: '/start',
methods: [ HttpMethod.POST ],
integration: new StepFunctionsStartExecutionIntegration({
stateMachine: state,
input: '$request.body.input',
timeout: Duration.seconds(10),
}),
});
```

### Private Integration

Private integrations enable integrating an HTTP API route with private resources in a VPC, such as Application Load Balancers or
Expand Down
141 changes: 141 additions & 0 deletions packages/@aws-cdk/aws-apigatewayv2-integrations/lib/http/aws.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { IRole } from '@aws-cdk/aws-iam';
import { IStateMachine } from '@aws-cdk/aws-stepfunctions';
import { AwsServiceIntegration, AwsServiceIntegrationProps } from './private/integration';

/**
* The common Step Functions integration resource for HTTP API
*/
abstract class StepFunctionsIntegration extends AwsServiceIntegration {

/**
*
* @internal
*/
protected _integrationService(): string {
return 'StepFunctions';
}
}

/**
* Step Functions StartExecution integration properties
*/
export interface StepFunctionsStartExecutionIntegrationProps extends AwsServiceIntegrationProps {

/**
* The state machine to be executed
*/
readonly stateMachine: IStateMachine;

/**
* The execution name
*
* @default - undefined
*/
readonly name?: string;

/**
* The input parameters of execution
*
* @default - undefined
*/
readonly input?: any;

/**
* The region of state machine
*
* @default - undefined
*/
readonly region?: string;
}

/**
* The StepFunctions-StartExecution integration resource for HTTP API
*/
export class StepFunctionsStartExecutionIntegration extends StepFunctionsIntegration {

constructor(private readonly _props: StepFunctionsStartExecutionIntegrationProps) {
super(_props);
}

/**
*
* @internal
*/
protected _integrationAction(): string {
return 'StartExecution';
}

/**
*
* @internal
*/
protected _fulfillRole(credentialsRole: IRole): void {
this._props.stateMachine.grantStartExecution(credentialsRole);
}

/**
*
* @internal
*/
protected _buildRequestParameters(): { [key: string]: any } {
return {
StateMachineArn: this._props.stateMachine.stateMachineArn,
Name: this._props.name,
Input: this._props.input,
Region: this._props.region,
};
}
}

/**
* Step Functions StartSyncExecution integration properties
*/
export interface StepFunctionsStartSyncExecutionIntegrationProps extends StepFunctionsStartExecutionIntegrationProps {

/**
* Passes the AWS X-Ray trace header. The trace header can also be passed in the request payload.
*
* @default - undefined
*/
readonly traceHeader?: string;
}

/**
* The StepFunctions-StartExecution integration resource for HTTP API
*/
export class StepFunctionsStartSyncExecutionIntegration extends StepFunctionsIntegration {

constructor(private readonly _props: StepFunctionsStartSyncExecutionIntegrationProps) {
super(_props);
}

/**
*
* @internal
*/
protected _integrationAction(): string {
return 'StartSyncExecution';
}

/**
*
* @internal
*/
protected _fulfillRole(credentialsRole: IRole): void {
this._props.stateMachine.grantStartExecution(credentialsRole);
}

/**
*
* @internal
*/
protected _buildRequestParameters(): { [key: string]: any } {
return {
StateMachineArn: this._props.stateMachine.stateMachineArn,
Name: this._props.name,
Input: this._props.input,
Region: this._props.region,
TraceHeader: this._props.traceHeader,
};
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { HttpMethod, IVpcLink } from '@aws-cdk/aws-apigatewayv2';
import { Duration } from '@aws-cdk/core';

/**
* Base options for private integration
Expand All @@ -16,4 +17,24 @@ export interface HttpPrivateIntegrationOptions {
* @default HttpMethod.ANY
*/
readonly method?: HttpMethod;
}

/**
* Common properties to initialize a new `HttpProxyIntegration`.
*/
export interface CommonIntegrationProps {

/**
* The description of the integration
*
* @default - undefined
*/
readonly description?: string;

/**
* Custom timeout for HTTP APIs
*
* @default - undefined
*/
readonly timeout?: Duration;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from './nlb';
export * from './service-discovery';
export * from './http-proxy';
export * from './lambda';
export * from './aws';
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {
IVpcLink,
} from '@aws-cdk/aws-apigatewayv2';
import * as ec2 from '@aws-cdk/aws-ec2';
import { IRole, Role, ServicePrincipal } from '@aws-cdk/aws-iam';
import { CommonIntegrationProps } from '../base-types';


/**
Expand Down Expand Up @@ -63,3 +65,65 @@ export abstract class HttpPrivateIntegration implements IHttpRouteIntegration {

public abstract bind(options: HttpRouteIntegrationBindOptions): HttpRouteIntegrationConfig;
}

/**
* Aws Service integration properties
*
* @internal
*/
export interface AwsServiceIntegrationProps extends CommonIntegrationProps {
}

/**
* The Aws Service integration resource for HTTP API
*
* @internal
*/
export abstract class AwsServiceIntegration implements IHttpRouteIntegration {

constructor(private readonly props: AwsServiceIntegrationProps) {
}

/**
*
* @internal
*/
protected abstract _fulfillRole(credentialsRole: IRole): void;

/**
*
* @internal
*/
protected abstract _buildRequestParameters(): { [key: string]: any };

/**
*
* @internal
*/
protected abstract _integrationService(): string;

/**
*
* @internal
*/
protected abstract _integrationAction(): string;

public bind(_options: HttpRouteIntegrationBindOptions): HttpRouteIntegrationConfig {

const role = new Role(_options.scope, 'Role', {
assumedBy: new ServicePrincipal('apigateway.amazonaws.com'),
});
this._fulfillRole(role);

return {
payloadFormatVersion: PayloadFormatVersion.VERSION_1_0, // 1.0 is required and is the only supported format
type: HttpIntegrationType.LAMBDA_PROXY,
subtype: `${this._integrationService()}-${this._integrationAction()}`,
credentials: role.roleArn,
timeout: this.props.timeout,
description: this.props.description,
requestParameters: this._buildRequestParameters(),
};
}

}
2 changes: 2 additions & 0 deletions packages/@aws-cdk/aws-apigatewayv2-integrations/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
"@aws-cdk/aws-iam": "0.0.0",
"@aws-cdk/aws-lambda": "0.0.0",
"@aws-cdk/aws-servicediscovery": "0.0.0",
"@aws-cdk/aws-stepfunctions": "0.0.0",
"@aws-cdk/core": "0.0.0",
"constructs": "^3.3.69"
},
Expand All @@ -94,6 +95,7 @@
"@aws-cdk/aws-iam": "0.0.0",
"@aws-cdk/aws-lambda": "0.0.0",
"@aws-cdk/aws-servicediscovery": "0.0.0",
"@aws-cdk/aws-stepfunctions": "0.0.0",
"@aws-cdk/core": "0.0.0",
"constructs": "^3.3.69"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import '@aws-cdk/assert-internal/jest';
import { HttpApi, HttpRoute, HttpRouteKey } from '@aws-cdk/aws-apigatewayv2';
import { StateMachine, Chain, Pass, StateMachineType } from '@aws-cdk/aws-stepfunctions';
import { Stack, Duration } from '@aws-cdk/core';
import { StepFunctionsStartExecutionIntegration, StepFunctionsStartSyncExecutionIntegration } from '../../lib';

describe('AwsServiceIntegration', () => {
test('StepFunctions-StartExecution', () => {
const stack = new Stack();
const api = new HttpApi(stack, 'HttpApi');

new HttpRoute(stack, 'StepFunctionsStartExeRoute', {
httpApi: api,
integration: new StepFunctionsStartExecutionIntegration({
stateMachine: stateMachine(stack),
name: 'MyExe',
input: '$request.body.input',
timeout: Duration.seconds(10),
description: 'Start execution of state machine',
}),
routeKey: HttpRouteKey.with('/start'),
});

expect(stack).toHaveResource('AWS::ApiGatewayV2::Integration', {
IntegrationType: 'AWS_PROXY',
CredentialsArn: {
'Fn::GetAtt': [
'StepFunctionsStartExeRouteRoleEC2F84D2',
'Arn',
],
},
IntegrationSubtype: 'StepFunctions-StartExecution',
PayloadFormatVersion: '1.0',
RequestParameters: {
StateMachineArn: {
Ref: 'MyStateMachine6C968CA5',
},
Input: '$request.body.input',
Name: 'MyExe',
},
TimeoutInMillis: 10000,
});
});

test('StepFunctions-StartSyncExecution', () => {
const stack = new Stack();
const api = new HttpApi(stack, 'HttpApi');

new HttpRoute(stack, 'StepFunctionsStartSyncExeRoute', {
httpApi: api,
integration: new StepFunctionsStartSyncExecutionIntegration({
stateMachine: stateMachine(stack),
input: {
a: 'b',
},
}),
routeKey: HttpRouteKey.with('/startSync'),
});

expect(stack).toHaveResource('AWS::ApiGatewayV2::Integration', {
IntegrationType: 'AWS_PROXY',
IntegrationSubtype: 'StepFunctions-StartSyncExecution',
PayloadFormatVersion: '1.0',
RequestParameters: {
StateMachineArn: {
Ref: 'MyStateMachine6C968CA5',
},
Input: {
a: 'b',
},
},
});
});
});

function stateMachine(stack: Stack): StateMachine {
return new StateMachine(stack, 'MyStateMachine', {
stateMachineName: 'MyStateMachine',
definition: Chain.start(new Pass(stack, 'Pass')),
stateMachineType: StateMachineType.STANDARD,
});
}
Loading

0 comments on commit 9697e9b

Please sign in to comment.