Skip to content

Commit

Permalink
fix(apigatewayv2): HttpApi and Route in different stacks creates cycl…
Browse files Browse the repository at this point in the history
…es (aws#13010)

When the Route construct with an integration is defined
in a separate stack from the one defining the HttpApi
construct, a circular dependency error is thrown.

The bug was introduced in - 855ce59 - where the
refactor of moving the creation of Integration resulted in
using the HttpApi as the scope of the Integration
construct, rather than the Route.

fixes aws#13021

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
hoegertn authored and NovakGu committed Feb 18, 2021
1 parent 9a8e8b5 commit 1009434
Show file tree
Hide file tree
Showing 9 changed files with 43 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,14 @@
[
"integrations/",
{
"Ref": "MyHttpApiHttpIntegration6f095b8469365f72e33fa33d9711b140C45F3B26"
"Ref": "MyHttpApiGETHttpIntegration6f095b8469365f72e33fa33d9711b140516EBE31"
}
]
]
}
}
},
"MyHttpApiHttpIntegration6f095b8469365f72e33fa33d9711b140C45F3B26": {
"MyHttpApiGETHttpIntegration6f095b8469365f72e33fa33d9711b140516EBE31": {
"Type": "AWS::ApiGatewayV2::Integration",
"Properties": {
"ApiId": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,7 @@
[
"integrations/",
{
"Ref": "HttpProxyPrivateApiHttpIntegration1a580b19954e4317026ffbce1f7d5ade82925DF0"
"Ref": "HttpProxyPrivateApiDefaultRouteHttpIntegration1a580b19954e4317026ffbce1f7d5ade7A32685B"
}
]
]
Expand All @@ -647,7 +647,7 @@
"SecurityGroupIds": []
}
},
"HttpProxyPrivateApiHttpIntegration1a580b19954e4317026ffbce1f7d5ade82925DF0": {
"HttpProxyPrivateApiDefaultRouteHttpIntegration1a580b19954e4317026ffbce1f7d5ade7A32685B": {
"Type": "AWS::ApiGatewayV2::Integration",
"Properties": {
"ApiId": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,14 @@
[
"integrations/",
{
"Ref": "LambdaProxyApiHttpIntegration70df0ec52c3e3b6bbc96e64ce3a05f24467606A4"
"Ref": "LambdaProxyApiDefaultRouteHttpIntegration70df0ec52c3e3b6bbc96e64ce3a05f24EE575CBA"
}
]
]
}
}
},
"LambdaProxyApiHttpIntegration70df0ec52c3e3b6bbc96e64ce3a05f24467606A4": {
"LambdaProxyApiDefaultRouteHttpIntegration70df0ec52c3e3b6bbc96e64ce3a05f24EE575CBA": {
"Type": "AWS::ApiGatewayV2::Integration",
"Properties": {
"ApiId": {
Expand Down Expand Up @@ -162,14 +162,14 @@
[
"integrations/",
{
"Ref": "HttpProxyApiHttpIntegration8eeecf9ecdb91f31bebf6bd54fb711a4F32A389A"
"Ref": "HttpProxyApiDefaultRouteHttpIntegration8eeecf9ecdb91f31bebf6bd54fb711a41921AB82"
}
]
]
}
}
},
"HttpProxyApiHttpIntegration8eeecf9ecdb91f31bebf6bd54fb711a4F32A389A": {
"HttpProxyApiDefaultRouteHttpIntegration8eeecf9ecdb91f31bebf6bd54fb711a41921AB82": {
"Type": "AWS::ApiGatewayV2::Integration",
"Properties": {
"ApiId": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,14 @@
[
"integrations/",
{
"Ref": "LambdaProxyApiHttpIntegration70df0ec52c3e3b6bbc96e64ce3a05f24467606A4"
"Ref": "LambdaProxyApiDefaultRouteHttpIntegration70df0ec52c3e3b6bbc96e64ce3a05f24EE575CBA"
}
]
]
}
}
},
"LambdaProxyApiHttpIntegration70df0ec52c3e3b6bbc96e64ce3a05f24467606A4": {
"LambdaProxyApiDefaultRouteHttpIntegration70df0ec52c3e3b6bbc96e64ce3a05f24EE575CBA": {
"Type": "AWS::ApiGatewayV2::Integration",
"Properties": {
"ApiId": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,7 @@
[
"integrations/",
{
"Ref": "HttpProxyPrivateApiHttpIntegration1a580b19954e4317026ffbce1f7d5ade82925DF0"
"Ref": "HttpProxyPrivateApiDefaultRouteHttpIntegration1a580b19954e4317026ffbce1f7d5ade7A32685B"
}
]
]
Expand All @@ -612,7 +612,7 @@
"SecurityGroupIds": []
}
},
"HttpProxyPrivateApiHttpIntegration1a580b19954e4317026ffbce1f7d5ade82925DF0": {
"HttpProxyPrivateApiDefaultRouteHttpIntegration1a580b19954e4317026ffbce1f7d5ade7A32685B": {
"Type": "AWS::ApiGatewayV2::Integration",
"Properties": {
"ApiId": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -588,14 +588,14 @@
[
"integrations/",
{
"Ref": "HttpProxyPrivateApiHttpIntegrationa5ec5390ca688d567e9449daf58afc6fB75CE02B"
"Ref": "HttpProxyPrivateApiDefaultRouteHttpIntegrationa5ec5390ca688d567e9449daf58afc6f6DEAA8A8"
}
]
]
}
}
},
"HttpProxyPrivateApiHttpIntegrationa5ec5390ca688d567e9449daf58afc6fB75CE02B": {
"HttpProxyPrivateApiDefaultRouteHttpIntegrationa5ec5390ca688d567e9449daf58afc6f6DEAA8A8": {
"Type": "AWS::ApiGatewayV2::Integration",
"Properties": {
"ApiId": {
Expand Down
8 changes: 4 additions & 4 deletions packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export interface IHttpApi extends IResource {
* Add a http integration
* @internal
*/
_addIntegration(config: HttpRouteIntegrationConfig): HttpIntegration;
_addIntegration(scope: Construct, config: HttpRouteIntegrationConfig): HttpIntegration;
}

/**
Expand Down Expand Up @@ -274,15 +274,15 @@ abstract class HttpApiBase extends Resource implements IHttpApi { // note that t
/**
* @internal
*/
public _addIntegration(config: HttpRouteIntegrationConfig): HttpIntegration {
const stringifiedConfig = JSON.stringify(Stack.of(this).resolve(config));
public _addIntegration(scope: Construct, config: HttpRouteIntegrationConfig): HttpIntegration {
const stringifiedConfig = JSON.stringify(Stack.of(scope).resolve(config));
const configHash = crypto.createHash('md5').update(stringifiedConfig).digest('hex');

if (configHash in this.httpIntegrations) {
return this.httpIntegrations[configHash];
}

const integration = new HttpIntegration(this, `HttpIntegration-${configHash}`, {
const integration = new HttpIntegration(scope, `HttpIntegration-${configHash}`, {
httpApi: this,
integrationType: config.type,
integrationUri: config.uri,
Expand Down
2 changes: 1 addition & 1 deletion packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ export class HttpRoute extends Resource implements IHttpRoute {
scope: this,
});

const integration = props.httpApi._addIntegration(config);
const integration = props.httpApi._addIntegration(this, config);

const authBindResult = props.authorizer ? props.authorizer.bind({
route: this,
Expand Down
27 changes: 24 additions & 3 deletions packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import '@aws-cdk/assert/jest';
import { Stack } from '@aws-cdk/core';
import { Stack, App } from '@aws-cdk/core';
import {
HttpApi, HttpAuthorizer, HttpAuthorizerType, HttpConnectionType, HttpIntegrationType, HttpMethod, HttpRoute, HttpRouteAuthorizerBindOptions,
HttpRouteAuthorizerConfig, HttpRouteIntegrationConfig, HttpRouteKey, IHttpRouteAuthorizer, IHttpRouteIntegration, PayloadFormatVersion,
Expand All @@ -25,7 +25,7 @@ describe('HttpRoute', () => {
[
'integrations/',
{
Ref: 'HttpApiHttpIntegrationcff2618c192d3bd8581dd2a4093464f6CDB667B8',
Ref: 'HttpRouteHttpIntegrationcff2618c192d3bd8581dd2a4093464f6FB1097D0',
},
],
],
Expand Down Expand Up @@ -115,6 +115,27 @@ describe('HttpRoute', () => {
expect(stack2).toCountResources('AWS::ApiGatewayV2::Integration', 1);
});

test('route defined in a separate stack does not create cycles', () => {
// GIVEN
const integration = new DummyIntegration();

// WHEN
const app = new App();
const stack1 = new Stack(app, 'ApiStack');
const httpApi = new HttpApi(stack1, 'HttpApi');

const stack2 = new Stack(app, 'RouteStack');
new HttpRoute(stack2, 'HttpRoute1', {
httpApi,
integration,
routeKey: HttpRouteKey.with('/books', HttpMethod.GET),
});

// THEN
expect(stack1).toCountResources('AWS::ApiGatewayV2::Integration', 0);
expect(stack2).toCountResources('AWS::ApiGatewayV2::Integration', 1);
});

test('throws when path not start with /', () => {
const stack = new Stack();
const httpApi = new HttpApi(stack, 'HttpApi');
Expand Down Expand Up @@ -253,4 +274,4 @@ class DummyAuthorizer implements IHttpRouteAuthorizer {
authorizationType: HttpAuthorizerType.JWT,
};
}
}
}

0 comments on commit 1009434

Please sign in to comment.