Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 13 additions & 0 deletions java/http-proxy-apigateway/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.classpath.txt
target
.classpath
.project
.idea
.settings
.vscode
*.iml

# CDK asset staging directory
.cdk.staging
cdk.out

56 changes: 56 additions & 0 deletions java/http-proxy-apigateway/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# HTTP Proxy APIGateway

<!--BEGIN STABILITY BANNER-->

---
![Stability: Stable](https://img.shields.io/badge/stability-Stable-success.svg?style=for-the-badge)
> **This is a stable example. It should successfully build out of the box**
>
> This example is built on Construct Libraries marked "Stable" and does not have any infrastructure prerequisites to build.
---

<!--END STABILITY BANNER-->

This example creates an API Gateway with proxy resources for 2 HTTP backends.
This is useful for scenarios when incoming requests must be routed to one or more backend API hosts.
An HTTP proxy integration enables direct interactions between clients and backends without any intervention from the API Gateway after the API method is set up.

> For more information on using HTTP proxy integrations with the APIGateway check out this [AWS tutorial](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-http.html).
> For demonstration purposes this CDK example deploys a solution that routes to a couple of test HTTP APIs.
> The 2 test HTTP APIs are implemented using lambdas exposed through function URLs.
> This example can be modified though, if you prefer to use your own HTTP backend APIs.
> To do that you can modify the `createHTTPTestAPIs` method in the [`HttpProxyApiGatewayStack`](src/main/java/com/myorg/HttpProxyApiGatewayStack.java) class to return a list of `ProxyResourceParameters` corresponding to your own resources.
## Build

To build this example, you need to be in this example's root directory. Then run the following:

```bash
npm install -g aws-cdk
npm install
cdk synth
```

This will install the necessary CDK, then this example's dependencies, and then build the CloudFormation template. The resulting CloudFormation template will be in the `cdk.out` directory.

## Deploy

Run `cdk deploy`.
This will deploy / redeploy the Stack to AWS.
After the CDK deployment is successful, 2 URL examples will be available in the terminal console:

- One for the `HttpProxyApiGatewayStack.HelloFunctionResourceExample` output
- One for the `HttpProxyApiGatewayStack.ByeFunctionResourceExample` output

At this point, you can copy each of the 2 URLs and paste them in the address bar of a browser to invoke the 2 APIs.
Also note that both URLs have the same host (the DNS of the new API Gateway created).

## Useful commands

* `mvn package` compile and run tests
* `cdk ls` list all stacks in the app
* `cdk synth` emits the synthesized CloudFormation template
* `cdk deploy` deploy this stack to your default AWS account/region
* `cdk diff` compare deployed stack with current state
* `cdk docs` open CDK documentation
76 changes: 76 additions & 0 deletions java/http-proxy-apigateway/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
{
"app": "mvn -e -q compile exec:java",
"watch": {
"include": [
"**"
],
"exclude": [
"README.md",
"cdk*.json",
"target",
"pom.xml",
"src/test"
]
},
"context": {
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
"@aws-cdk/core:checkSecretUsage": true,
"@aws-cdk/core:target-partitions": [
"aws",
"aws-cn"
],
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
"@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
"@aws-cdk/aws-iam:minimizePolicies": true,
"@aws-cdk/core:validateSnapshotRemovalPolicy": true,
"@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
"@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
"@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
"@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
"@aws-cdk/core:enablePartitionLiterals": true,
"@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
"@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
"@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true,
"@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true,
"@aws-cdk/aws-route53-patters:useCertificate": true,
"@aws-cdk/customresources:installLatestAwsSdkDefault": false,
"@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true,
"@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true,
"@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true,
"@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true,
"@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true,
"@aws-cdk/aws-redshift:columnId": true,
"@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true,
"@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true,
"@aws-cdk/aws-apigateway:requestValidatorUniqueId": true,
"@aws-cdk/aws-kms:aliasNameRef": true,
"@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true,
"@aws-cdk/core:includePrefixInUniqueNameGeneration": true,
"@aws-cdk/aws-efs:denyAnonymousAccess": true,
"@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true,
"@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true,
"@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true,
"@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true,
"@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true,
"@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true,
"@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true,
"@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": true,
"@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": true,
"@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": true,
"@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": true,
"@aws-cdk/aws-eks:nodegroupNameAttribute": true,
"@aws-cdk/aws-ec2:ebsDefaultGp3Volume": true,
"@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": true,
"@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": false,
"@aws-cdk/aws-s3:keepNotificationInImportedBucket": false,
"@aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions": true,
"@aws-cdk/aws-dynamodb:resourcePolicyPerReplica": true,
"@aws-cdk/aws-ec2:ec2SumTimeoutEnabled": true,
"@aws-cdk/aws-appsync:appSyncGraphQLAPIScopeLambdaPermission": true,
"@aws-cdk/aws-rds:setCorrectValueForDatabaseInstanceReadReplicaInstanceResourceId": true,
"@aws-cdk/core:cfnIncludeRejectComplexResourceUpdateCreatePolicyIntrinsics": true,
"@aws-cdk/aws-lambda-nodejs:sdkV3ExcludeSmithyPackages": true,
"@aws-cdk/aws-stepfunctions-tasks:fixRunEcsTaskPolicy": true
}
}
62 changes: 62 additions & 0 deletions java/http-proxy-apigateway/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>

<groupId>com.myorg</groupId>
<artifactId>http-proxy-apigateway</artifactId>
<version>0.1</version>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<cdk.version>2.171.1</cdk.version>
<constructs.version>[10.0.0,11.0.0)</constructs.version>
<junit.version>5.7.1</junit.version>
<maven.compiler.target>22</maven.compiler.target>
<maven.compiler.source>22</maven.compiler.source>
</properties>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<release>17</release>
</configuration>
</plugin>

<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<mainClass>com.myorg.HttpProxyApiGatewayApp</mainClass>
</configuration>
</plugin>
</plugins>
</build>

<dependencies>
<!-- AWS Cloud Development Kit -->
<dependency>
<groupId>software.amazon.awscdk</groupId>
<artifactId>aws-cdk-lib</artifactId>
<version>${cdk.version}</version>
</dependency>

<dependency>
<groupId>software.constructs</groupId>
<artifactId>constructs</artifactId>
<version>${constructs.version}</version>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.myorg;

import software.amazon.awscdk.App;
import software.amazon.awscdk.StackProps;

public class HttpProxyApiGatewayApp {
public static void main(final String[] args) {
var app = new App();
var stackProps = StackProps.builder()
.stackName("HttpProxyApiGatewayStack")
.build();
new HttpProxyApiGatewayStack(app, "HttpProxyApiGatewayStack", stackProps);
app.synth();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package com.myorg;

import software.amazon.awscdk.CfnOutput;
import software.amazon.awscdk.RemovalPolicy;
import software.amazon.awscdk.Stack;
import software.amazon.awscdk.StackProps;
import software.amazon.awscdk.services.apigateway.*;
import software.amazon.awscdk.services.lambda.*;
import software.constructs.Construct;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static software.amazon.awscdk.services.apigatewayv2.HttpMethod.ANY;
import static software.amazon.awscdk.services.lambda.Runtime.PYTHON_3_12;

public class HttpProxyApiGatewayStack extends Stack {

public record ProxyResourceParameters(String resourceId, String baseUrl, String httpMethod, String exampleRequest) {

}

private final RestApi restApi;

public HttpProxyApiGatewayStack(final Construct scope, final String id, final StackProps props) {
super(scope, id, props);
restApi = RestApi.Builder.create(this, "RestApi")
.restApiName("RestApi")
.cloudWatchRole(true)
.cloudWatchRoleRemovalPolicy(RemovalPolicy.DESTROY)
.endpointTypes(List.of(EndpointType.EDGE))
.build();
createHTTPTestAPIs().forEach(this::createProxyResource);
}

private List<ProxyResourceParameters> createHTTPTestAPIs() {
return Map.of(
"HelloFunction", "/hello?from=AWS",
"ByeFunction", "/bye?from=AWS"
).entrySet().stream()
.map(functionEntry -> {
var functionName = functionEntry.getKey();
var parameters = functionEntry.getValue();
var function = Function.Builder.create(this, functionName)
.functionName(functionName)
.code(InlineCode.fromInline(getInlineCode("src/main/resources/lambdas/" + functionName + ".py")))
.handler("index.handler")
.runtime(PYTHON_3_12)
.build();
var lambdaFunctionAlias = Alias.Builder.create(this, functionName + "ProdAlias")
.aliasName("Prod")
.version(function.getCurrentVersion())
.build();
var functionURL = FunctionUrl.Builder.create(this, functionName + "Url")
.function(lambdaFunctionAlias)
.authType(FunctionUrlAuthType.NONE)
.invokeMode(InvokeMode.BUFFERED)
.cors(
FunctionUrlCorsOptions.builder()
.allowedOrigins(List.of("*"))
.allowedMethods(List.of(HttpMethod.GET))
.allowedHeaders(List.of("*"))
.build()
)
.build();
return new ProxyResourceParameters(functionName + "Resource", functionURL.getUrl(), ANY.name(), parameters);
}).collect(ArrayList::new, List::add, List::addAll);
}

private void createProxyResource(ProxyResourceParameters proxyResourceParameters) {
var parentProxyResource = restApi.getRoot().addResource(proxyResourceParameters.resourceId);
var proxyResource = ProxyResource.Builder.create(this, proxyResourceParameters.resourceId + "ProxyResource")
.parent(parentProxyResource)
.anyMethod(false)
.build();
var integrationOptions = IntegrationOptions.builder()
.requestParameters(
Map.of(
"integration.request.path.proxy", "method.request.path.proxy"
)
)
.build();
var httpIntegrationProps = HttpIntegrationProps.builder()
.proxy(true)
.httpMethod(proxyResourceParameters.httpMethod)
.options(integrationOptions)
.build();
var methodOptions = MethodOptions.builder()
.requestParameters(
Map.of(
"method.request.path.proxy", true
)
)
.build();
var httpIntegration = new HttpIntegration(proxyResourceParameters.baseUrl + "{proxy}", httpIntegrationProps);
proxyResource.addMethod(proxyResourceParameters.httpMethod, httpIntegration, methodOptions);
var proxyResourceUrl = restApi.urlForPath(proxyResource.getPath());
CfnOutput.Builder.create(this, proxyResourceParameters.resourceId + "ProxyUrl")
.value(proxyResourceUrl)
.build();
CfnOutput.Builder.create(this, proxyResourceParameters.resourceId + "Example")
.value(proxyResourceUrl.replace("/{proxy+}", proxyResourceParameters.exampleRequest))
.build();
}

private String getInlineCode(String path) {
try {
return new String(Files.readAllBytes(Path.of(path)));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import json
def handler(event, context):
fromValue = event.get('queryStringParameters', {}).get('from', 'Lambda')
return {
'statusCode': 200,
'body': json.dumps('Bye from ' + fromValue + '!')
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import json
def handler(event, context):
fromValue = event.get('queryStringParameters', {}).get('from', 'Lambda')
return {
'statusCode': 200,
'body': json.dumps('Hello from ' + fromValue + '!')
}
Loading
Loading