Skip to content

Commit

Permalink
chore(apigatewayv2): integration api re-organization (aws#17752)
Browse files Browse the repository at this point in the history
There are three major changes.

`HttpRouteIntegration` (and its sibling `WebSocketRouteIntegration`)
creates a CDK construct (`HttpIntegration` and `WebSocketIntegration`)
as part of its bind operation. The id to this CDK construct is
determined by hashing the results of the bind.
Using hashes makes the construct id fragile/sensitive, consequently the
CFN resource's logical id fragile.
The fragility comes mainly from the question - have we hashed all of the
expected properties that should be hashed, and nothing extra?
If we have not hashed properties that should be there, or hashed too
much, we end up with a hash change, hence resource replacement that
is unexpected.

This commit changes this approach and asks the user to provide the
construct's id. This is more aligned with the current CDK expectation
that users provide an id when initializing constructs.
We just don't have a good way to validate that our hashing is accurate,
so let's not do it at all. This change makes the user provide a unique
name within a scope, which is already a standard requirement for CDK
constructs.

Secondly, the ergonomics of specific integration implementations, such
as, `LambdaProxyIntegration`, `HttpAlbIntegration`, etc. is modified so
that the integrating primitive is moved out of the 'props', and to the
constructor.
The API ergonomics of this feels much better than having to always
provide a 'props'.

Since this package contains constructs around both http api and
websocket api, the convention to follow is that all classes specific to
the former will be prefixed with `Http` and the latter will be prefixed
with `WebSocket`.
Bring the integration classes `LambdaProxyIntegration` and
`HttpProxyIntegration` in line with this convention. These are renamed
to `HttpLambdaIntegration` and `HttpUrlIntegration` respectively.

BREAKING CHANGE: The `HttpIntegration` and `WebSocketIntegration`
classes require an "id" parameter to be provided during its initialization.
* **apigatewayv2-integrations:** The `LambdaWebSocketIntegration` is now
  renamed to `WebSocketLambdaIntegration`. The new class accepts the
  handler to the target lambda function directly in its constructor.
* **apigatewayv2-integrations:** `HttpProxyIntegration` and
  `HttpProxyIntegrationProps` are now renamed to `HttpUrlIntegration`
  and `HttpUrlIntegrationProps` respectively. The new class accepts the
  target url directly in its constructor.
* **apigatewayv2-integrations:** `LambdaProxyIntegration` and
  `LambdaProxyIntegrationProps` are now renamed to
  `HttpLambdaIntegration` and `HttpLambdaIntegrationProps` respectively.
  The new class accepts the lambda function handler directly in its
  constructor.
* **apigatewayv2-integrations:** `HttpAlbIntegration` now accepts the
  ELB listener directly in its constructor.
* **apigatewayv2-integrations:** `HttpNlbIntegration` now accepts the
  ELB listener directly in its constructor.
* **apigatewayv2-integrations:** `HttpServiceDiscoveryIntegration` now
  accepts the service discovery Service directly in its constructor.
* **apigatewayv2-authorizers:** `UserPoolAuthorizerProps` is now
  renamed to `HttpUserPoolAuthorizerProps`.

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
Niranjan Jayakar authored Nov 29, 2021
1 parent a1685c6 commit 29039e8
Show file tree
Hide file tree
Showing 45 changed files with 281 additions and 355 deletions.
36 changes: 11 additions & 25 deletions packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ The example below showcases default authorization, along with route authorizatio

```ts
import { HttpJwtAuthorizer } from '@aws-cdk/aws-apigatewayv2-authorizers';
import { HttpProxyIntegration } from '@aws-cdk/aws-apigatewayv2-integrations';
import { HttpUrlIntegration } from '@aws-cdk/aws-apigatewayv2-integrations';

const authorizer = new HttpJwtAuthorizer({
jwtAudience: ['3131231'],
Expand All @@ -84,34 +84,26 @@ const api = new apigwv2.HttpApi(this, 'HttpApi', {
});

api.addRoutes({
integration: new HttpProxyIntegration({
url: 'https://get-books-proxy.myproxy.internal',
}),
integration: new HttpUrlIntegration('BooksIntegration', 'https://get-books-proxy.myproxy.internal'),
path: '/books',
methods: [apigwv2.HttpMethod.GET],
});

api.addRoutes({
integration: new HttpProxyIntegration({
url: 'https://get-books-proxy.myproxy.internal',
}),
integration: new HttpUrlIntegration('BooksIdIntegration', 'https://get-books-proxy.myproxy.internal'),
path: '/books/{id}',
methods: [apigwv2.HttpMethod.GET],
});

api.addRoutes({
integration: new HttpProxyIntegration({
url: 'https://get-books-proxy.myproxy.internal',
}),
integration: new HttpUrlIntegration('BooksIntegration', 'https://get-books-proxy.myproxy.internal'),
path: '/books',
methods: [apigwv2.HttpMethod.POST],
authorizationScopes: ['write:books']
});

api.addRoutes({
integration: new HttpProxyIntegration({
url: 'https://get-books-proxy.myproxy.internal',
}),
integration: new HttpUrlIntegration('LoginIntegration', 'https://get-books-proxy.myproxy.internal'),
path: '/login',
methods: [apigwv2.HttpMethod.POST],
authorizer: new apigwv2.HttpNoneAuthorizer(),
Expand All @@ -136,7 +128,7 @@ Clients that fail authorization are presented with either 2 responses:

```ts
import { HttpJwtAuthorizer } from '@aws-cdk/aws-apigatewayv2-authorizers';
import { HttpProxyIntegration } from '@aws-cdk/aws-apigatewayv2-integrations';
import { HttpUrlIntegration } from '@aws-cdk/aws-apigatewayv2-integrations';

const authorizer = new HttpJwtAuthorizer({
jwtAudience: ['3131231'],
Expand All @@ -146,9 +138,7 @@ const authorizer = new HttpJwtAuthorizer({
const api = new apigwv2.HttpApi(this, 'HttpApi');

api.addRoutes({
integration: new HttpProxyIntegration({
url: 'https://get-books-proxy.myproxy.internal',
}),
integration: new HttpUrlIntegration('BooksIntegration', 'https://get-books-proxy.myproxy.internal'),
path: '/books',
authorizer,
});
Expand All @@ -165,7 +155,7 @@ pools as authorizer](https://docs.aws.amazon.com/apigateway/latest/developerguid
```ts
import * as cognito from '@aws-cdk/aws-cognito';
import { HttpUserPoolAuthorizer } from '@aws-cdk/aws-apigatewayv2-authorizers';
import { HttpProxyIntegration } from '@aws-cdk/aws-apigatewayv2-integrations';
import { HttpUrlIntegration } from '@aws-cdk/aws-apigatewayv2-integrations';

const userPool = new cognito.UserPool(this, 'UserPool');
const userPoolClient = userPool.addClient('UserPoolClient');
Expand All @@ -178,9 +168,7 @@ const authorizer = new HttpUserPoolAuthorizer({
const api = new apigwv2.HttpApi(this, 'HttpApi');

api.addRoutes({
integration: new HttpProxyIntegration({
url: 'https://get-books-proxy.myproxy.internal',
}),
integration: new HttpUrlIntegration('BooksIntegration', 'https://get-books-proxy.myproxy.internal'),
path: '/books',
authorizer,
});
Expand All @@ -195,7 +183,7 @@ Lambda authorizers depending on their response, fall into either two types - Sim

```ts
import { HttpLambdaAuthorizer, HttpLambdaResponseType } from '@aws-cdk/aws-apigatewayv2-authorizers';
import { HttpProxyIntegration } from '@aws-cdk/aws-apigatewayv2-integrations';
import { HttpUrlIntegration } from '@aws-cdk/aws-apigatewayv2-integrations';

// This function handles your auth logic
declare const authHandler: lambda.Function;
Expand All @@ -209,9 +197,7 @@ const authorizer = new HttpLambdaAuthorizer({
const api = new apigwv2.HttpApi(this, 'HttpApi');

api.addRoutes({
integration: new HttpProxyIntegration({
url: 'https://get-books-proxy.myproxy.internal',
}),
integration: new HttpUrlIntegration('BooksIntegration', 'https://get-books-proxy.myproxy.internal'),
path: '/books',
authorizer,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { IUserPool, IUserPoolClient } from '@aws-cdk/aws-cognito';
import { Stack, Token } from '@aws-cdk/core';

/**
* Properties to initialize UserPoolAuthorizer.
* Properties to initialize HttpUserPoolAuthorizer.
*/
export interface UserPoolAuthorizerProps {
export interface HttpUserPoolAuthorizerProps {
/**
* The user pool clients that should be used to authorize requests with the user pool.
*/
Expand Down Expand Up @@ -43,7 +43,7 @@ export interface UserPoolAuthorizerProps {
export class HttpUserPoolAuthorizer implements IHttpRouteAuthorizer {
private authorizer?: HttpAuthorizer;

constructor(private readonly props: UserPoolAuthorizerProps) {
constructor(private readonly props: HttpUserPoolAuthorizerProps) {
}

public bind(options: HttpRouteAuthorizerBindOptions): HttpRouteAuthorizerConfig {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"AutoDeploy": true
}
},
"MyHttpApiGETAuthorizerIntegMyHttpApiGET16D02385PermissionBB02EBFE": {
"MyHttpApiGETRootIntegrationPermission81613491": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"Action": "lambda:InvokeFunction",
Expand Down Expand Up @@ -54,7 +54,7 @@
}
}
},
"MyHttpApiGETHttpIntegration6f095b8469365f72e33fa33d9711b140516EBE31": {
"MyHttpApiGETRootIntegration5068C5B0": {
"Type": "AWS::ApiGatewayV2::Integration",
"Properties": {
"ApiId": {
Expand Down Expand Up @@ -87,7 +87,7 @@
[
"integrations/",
{
"Ref": "MyHttpApiGETHttpIntegration6f095b8469365f72e33fa33d9711b140516EBE31"
"Ref": "MyHttpApiGETRootIntegration5068C5B0"
}
]
]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as path from 'path';
import { HttpApi, HttpMethod } from '@aws-cdk/aws-apigatewayv2';
import { LambdaProxyIntegration } from '@aws-cdk/aws-apigatewayv2-integrations';
import { HttpLambdaIntegration } from '@aws-cdk/aws-apigatewayv2-integrations';
import * as lambda from '@aws-cdk/aws-lambda';
import { App, Stack, CfnOutput } from '@aws-cdk/core';
import { HttpLambdaAuthorizer, HttpLambdaResponseType } from '../../lib';
Expand Down Expand Up @@ -40,7 +40,7 @@ const handler = new lambda.Function(stack, 'lambda', {
httpApi.addRoutes({
path: '/',
methods: [HttpMethod.GET],
integration: new LambdaProxyIntegration({ handler }),
integration: new HttpLambdaIntegration('RootIntegration', handler),
authorizer,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"AutoDeploy": true
}
},
"MyHttpApiGETAuthorizerIntegMyHttpApiGET16D02385PermissionBB02EBFE": {
"MyHttpApiGETRootIntegratinPermissionCEEEB498": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"Action": "lambda:InvokeFunction",
Expand Down Expand Up @@ -54,7 +54,7 @@
}
}
},
"MyHttpApiGETHttpIntegration6f095b8469365f72e33fa33d9711b140516EBE31": {
"MyHttpApiGETRootIntegratin93150A89": {
"Type": "AWS::ApiGatewayV2::Integration",
"Properties": {
"ApiId": {
Expand Down Expand Up @@ -87,7 +87,7 @@
[
"integrations/",
{
"Ref": "MyHttpApiGETHttpIntegration6f095b8469365f72e33fa33d9711b140516EBE31"
"Ref": "MyHttpApiGETRootIntegratin93150A89"
}
]
]
Expand All @@ -101,10 +101,10 @@
"Ref": "MyHttpApi8AEAAC21"
},
"AuthorizerType": "JWT",
"Name": "UserPoolAuthorizer",
"IdentitySource": [
"$request.header.Authorization"
],
"Name": "UserPoolAuthorizer",
"JwtConfiguration": {
"Audience": [
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/// !cdk-integ pragma:ignore-assets
import * as path from 'path';
import { HttpApi, HttpMethod } from '@aws-cdk/aws-apigatewayv2';
import { LambdaProxyIntegration } from '@aws-cdk/aws-apigatewayv2-integrations';
import { HttpLambdaIntegration } from '@aws-cdk/aws-apigatewayv2-integrations';
import * as cognito from '@aws-cdk/aws-cognito';
import * as lambda from '@aws-cdk/aws-lambda';
import { App, Stack } from '@aws-cdk/core';
Expand Down Expand Up @@ -37,6 +37,6 @@ const handler = new lambda.Function(stack, 'lambda', {
httpApi.addRoutes({
path: '/',
methods: [HttpMethod.GET],
integration: new LambdaProxyIntegration({ handler }),
integration: new HttpLambdaIntegration('RootIntegratin', handler),
authorizer,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { HttpIntegrationType, HttpRouteIntegration, HttpRouteIntegrationBindOptions, PayloadFormatVersion } from '@aws-cdk/aws-apigatewayv2';

export class DummyRouteIntegration extends HttpRouteIntegration {
constructor() {
super('DummyRouteIntegration');
}

public bind(_: HttpRouteIntegrationBindOptions) {
return {
payloadFormatVersion: PayloadFormatVersion.VERSION_2_0,
type: HttpIntegrationType.HTTP_PROXY,
uri: 'some-uri',
};
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Template } from '@aws-cdk/assertions';
import { HttpApi, HttpIntegrationType, HttpRouteIntegrationBindOptions, HttpRouteIntegration, PayloadFormatVersion } from '@aws-cdk/aws-apigatewayv2';
import { HttpApi } from '@aws-cdk/aws-apigatewayv2';
import { Stack } from '@aws-cdk/core';
import { HttpJwtAuthorizer } from '../../lib';
import { DummyRouteIntegration } from './integration';

describe('HttpJwtAuthorizer', () => {
test('default', () => {
Expand Down Expand Up @@ -58,13 +59,3 @@ describe('HttpJwtAuthorizer', () => {
Template.fromStack(stack).resourceCountIs('AWS::ApiGatewayV2::Authorizer', 1);
});
});

class DummyRouteIntegration extends HttpRouteIntegration {
public bind(_: HttpRouteIntegrationBindOptions) {
return {
payloadFormatVersion: PayloadFormatVersion.VERSION_2_0,
type: HttpIntegrationType.HTTP_PROXY,
uri: 'some-uri',
};
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Match, Template } from '@aws-cdk/assertions';
import { HttpApi, HttpIntegrationType, HttpRouteIntegrationBindOptions, HttpRouteIntegration, PayloadFormatVersion } from '@aws-cdk/aws-apigatewayv2';
import { HttpApi } from '@aws-cdk/aws-apigatewayv2';
import { Code, Function, Runtime } from '@aws-cdk/aws-lambda';
import { Duration, Stack } from '@aws-cdk/core';
import { HttpLambdaAuthorizer, HttpLambdaResponseType } from '../../lib';
import { DummyRouteIntegration } from './integration';

describe('HttpLambdaAuthorizer', () => {

Expand Down Expand Up @@ -169,13 +170,3 @@ describe('HttpLambdaAuthorizer', () => {
});
});
});

class DummyRouteIntegration extends HttpRouteIntegration {
public bind(_: HttpRouteIntegrationBindOptions) {
return {
payloadFormatVersion: PayloadFormatVersion.VERSION_2_0,
type: HttpIntegrationType.HTTP_PROXY,
uri: 'some-uri',
};
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Template } from '@aws-cdk/assertions';
import { HttpApi, HttpIntegrationType, HttpRouteIntegrationBindOptions, HttpRouteIntegration, PayloadFormatVersion } from '@aws-cdk/aws-apigatewayv2';
import { HttpApi } from '@aws-cdk/aws-apigatewayv2';
import { UserPool } from '@aws-cdk/aws-cognito';
import { Stack } from '@aws-cdk/core';
import { HttpUserPoolAuthorizer } from '../../lib';
import { DummyRouteIntegration } from './integration';

describe('HttpUserPoolAuthorizer', () => {
test('default', () => {
Expand Down Expand Up @@ -111,13 +112,3 @@ describe('HttpUserPoolAuthorizer', () => {
});
});
});

class DummyRouteIntegration extends HttpRouteIntegration {
public bind(_: HttpRouteIntegrationBindOptions) {
return {
payloadFormatVersion: PayloadFormatVersion.VERSION_2_0,
type: HttpIntegrationType.HTTP_PROXY,
uri: 'some-uri',
};
}
}
Loading

0 comments on commit 29039e8

Please sign in to comment.