Skip to content

Commit

Permalink
feat(new construct): aws-openapigateway-lambda (#912)
Browse files Browse the repository at this point in the history
* README for new openapi-based apigatway-to-lambda construct.

* fix(core) prevent lambda id conflict with multiple buildLambdaFunction() calls in the same stack (#910)

* chore(release): 2.33.0

* chore(release prep): Update CHANGELOG.md and align-version.js

* Freeze @types/node version in cdk-integ

* Freeze @types/node version in cdk-integ

* Update step functions integ tests

* chore(core): Add warnings about using core functions from outside of Solutions Constructs (#917)

* Add file header warning

* Add function header comments

* Use latest CDK to address .NET version issue

* Fix 2 typos

* One last typo

* fix(StepFunctions): Address LogGroup behavior problems (#922)

* Implementation

* Clean up some code cruft

* Update package.json

* chore(release): 2.34.0

* chore(release-prep): Updated CHANGELOG.md and align-version.js

* chore(release-prep): align-version.js

* chore(core): migrate to assertions (#929)

* First two test updates

* chore(core): migrate to assertions

* Remove old lib, final few modules

* Update the cloudfront-to-s3 construct to correctly set the logging bucket property. (#930)

* Update README

* Update openapigateway-to-lambda README

* Update README/architecture for openapigateway-to-lambda.

* Add openapigateway-to-lambda code and initial tests

* update openapigateway-to-lambda package.json dependency versions.

* Update openapigateway-to-lambda

* Update openapigateway README

* don't depend on NodeJsFunction docker env

* Update openapigateway-to-lambda README to reflect actual construct API.

* temp commit

* Update snapshot test for openapigateway-to-lambda construct.

* fix package.json version field

* update snapshot

* update openapigateway-to-lambda custom resource to suppress standard cfn nag warnings.

* [wip] resources project

* fix dependency self reference on new resources module

* fix dependency self reference on new resources module

* Add integ test to the template writer resource.

* Add integ tests to template-writer resource.

* Update template-writer resource integ tests to clean up test buckets automatically

* Add additinal tests to get 100% coverage on aws-openapigateway-lambda

* Add additional integration tests to template resource writer and aws-openapigateway-lambda.

* remove eslintignore line that was obsolete

* cleanup eslint ignore and update openapigateway-to-lambda props.

* Update python/java code samples for openapigateway-to-lambda.

* Update openapigateway-to-lambda README

* Update resources README

* Update resources integ test

* update resources integ test snapshot

* Update integ test snapshots for aws-openapigateway-lambda.

* Update aws-openapigateway-lambda construct to trigger api deployments anytime the incoming api template changes or any time the lambda functions change.

* Add new integ test for cognito authorizer on aws-openapigateway-lambda construct.

* address minor pr feedback.

* Remove integration tests that use BucketDeployment as the asset hash of the AwsCliLayer changes outside our control.

* Remove integration tests that use BucketDeployment as the asset hash of the AwsCliLayer changes outside our control.

* Address pr feedback.

Add new template writer integration test for transforming large template with several thousand substitutions
Several new comments/documentation to make the obscure less so, hopefully>

* Add additional tests to aws-openapigateway-lambda construct.

* Update integ tests after latest cdk lib update.

* Add optional construct id parameter to the buildLambdaFunction function.

* Update aws-openapigateway-lambda property descriptions to better explain optional custom resource values.

---------

Co-authored-by: Andriy <andriy.frankevych@gmail.com>
Co-authored-by: AWS Solutions Constructs Automation <aws-solutions-constructs-team@amazon.com>
Co-authored-by: biffgaut <biffgaut@amazon.com>
Co-authored-by: biffgaut <78155736+biffgaut@users.noreply.github.com>
  • Loading branch information
5 people committed Jul 26, 2023
1 parent 045a1a1 commit 09465d6
Show file tree
Hide file tree
Showing 40 changed files with 31,905 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
lib/*.js
test/*.js
*.d.ts
coverage
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
lib/*.js
test/*.js
*.js.map
*.d.ts
node_modules
*.generated.ts
dist
.jsii

.LAST_BUILD
.nyc_output
coverage
.nycrc
.LAST_PACKAGE
*.snk
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Exclude typescript source and config
*.ts
tsconfig.json
coverage
.nyc_output
*.tgz
*.snk
*.tsbuildinfo

# Include javascript files and typescript declarations
!*.js
!*.d.ts

# Exclude jsii outdir
dist

# Include .jsii
!.jsii

# Include .jsii
!.jsii
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
# aws-openapigateway-lambda module
<!--BEGIN STABILITY BANNER-->

---

![Stability: Experimental](https://img.shields.io/badge/stability-Experimental-important.svg?style=for-the-badge)

---
<!--END STABILITY BANNER-->

| **Reference Documentation**:| <span style="font-weight: normal">https://docs.aws.amazon.com/solutions/latest/constructs/</span>|
|:-------------|:-------------|
<div style="height:8px"></div>

| **Language** | **Package** |
|:-------------|-----------------|
|![Python Logo](https://docs.aws.amazon.com/cdk/api/latest/img/python32.png) Python|`aws_solutions_constructs.aws_openapigateway_lambda`|
|![Typescript Logo](https://docs.aws.amazon.com/cdk/api/latest/img/typescript32.png) Typescript|`@aws-solutions-constructs/aws-openapigateway-lambda`|
|![Java Logo](https://docs.aws.amazon.com/cdk/api/latest/img/java32.png) Java|`software.amazon.awsconstructs.services.openapigatewaylambda`|

## Overview

This AWS Solutions Construct implements an Amazon API Gateway REST API defined by an OpenAPI specificiation file connected to an AWS Lambda function.

Here is a minimal deployable pattern definition:

Typescript
``` typescript
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { OpenApiApiGatewayToLambda } from './construct';
import { Asset } from 'aws-cdk-lib/aws-s3-assets';
import * as path from 'path';
import * as lambda from 'aws-cdk-lib/aws-lambda';

const apiDefinitionAsset = new Asset(this, 'ApiDefinitionAsset', {
path: path.join(__dirname, 'openapispec.yaml')
});

new OpenApiGatewayToLambda(this, 'OpenApiGatewayToLambda', {
apiDefinitionAsset,
apiIntegrations: [
{
id: 'MessagesHandler',
lambdaFunctionProps: {
runtime: lambda.Runtime.NODEJS_18_X,
handler: 'index.handler',
code: lambda.Code.fromAsset(`${__dirname}/messages-lambda`),
}
}
]
});
```

Python
``` python
from aws_solutions_constructs.aws_openapigateway_lambda import ApiGatewayToLambda
from aws_cdk import (
Stack
)

import aws_cdk.aws_s3_assets as s3_assets
import aws_cdk.aws_lambda as lambda_
from constructs import Construct
from .api_definition import ApiDefinition

api_definition_asset = s3_assets.Asset(self, "ApiDefinitionAsset",
path="openapispec.yaml"
)

api_integration = ApiDefinition("MessagesHandler", (
runtime=lambda_.Runtime.NODEJS_18_X,
handler="index.handler",
code=lambda_.Code.from_inline("exports.handler = handler.toString()")
))

ApiGatewayToLambda(self, "OpenApiGatewayToLambda",
api_definition_asset = api_definition_asset,
api_integrations = [ api_integration]
)
```

Java
``` java
import software.amazon.awscdk.services.lambda.Code;
import software.amazon.awscdk.services.lambda.FunctionProps;
import software.amazon.awscdk.services.s3.assets.Asset;
import software.amazon.awscdk.services.s3.assets.AssetProps;
import software.amazon.awsconstructs.services.openapigatewaylambda.ApiIntegration;
import software.amazon.awsconstructs.services.openapigatewaylambda.OpenApiGatewayToLambda;
import software.amazon.awsconstructs.services.openapigatewaylambda.OpenApiGatewayToLambdaProps;
import software.constructs.Construct;
import software.amazon.awscdk.Stack;
import software.amazon.awscdk.StackProps;

import java.util.Collections;

import static software.amazon.awscdk.services.lambda.Runtime.NODEJS_18_X;

final Asset apiDefintionAsset = new Asset(this, "ApiDefinition", AssetProps.builder().path("openapispec.yaml").build());

final ApiIntegration apiIntegration = ApiIntegration.builder()
.id("MessagesHandler")
.lambdaFunctionProps(new FunctionProps.Builder()
.runtime(NODEJS_18_X)
.code(Code.fromAsset("lambda"))
.handler("index.handler")
.build())
.build();

new OpenApiGatewayToLambda(this, "OpenApiGatewayToLambda", OpenApiGatewayToLambdaProps.builder()
.apiDefinitionAsset(apiDefintionAsset)
.apiIntegrations(Collections.singletonList(apiIntegration))
.build());
```

## Pattern Construct Props

| **Name** | **Type** | **Description** |
|:-------------|:----------------|-----------------|
|apiGatewayProps?|[`apigateway.RestApiBaseProps`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_apigateway.RestApiBaseProps.html)|Optional user-provided props to override the default props for the API.|
|apiDefinitionBucket?|[`s3.IBucket`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3.IBucket.html)|S3 Bucket where the OpenAPI spec file is located. When specifying this property, `apiDefinitionKey` must also be specified.|
|apiDefinitionKey?|`string`|S3 Object name of the OpenAPI spec file. When specifying this property, `apiDefinitionBucket` must also be specified.|
|apiDefinitionAsset?|[`aws_s3_assets.Asset`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3_assets.Asset.html)|Local file asset of the OpenAPI spec file.|
|apiIntegrations|`ApiIntegration[]`|One or more key-value pairs that contain an id for the api integration and either an existing lambda function or an instance of the LambdaProps. Please see the `Overview of how the OpenAPI file transformation works` section below for more usage details.|
|logGroupProps?|[`logs.LogGroupProps`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_logs.LogGroupProps.html)|User provided props to override the default props for for the CloudWatchLogs LogGroup.|

## Pattern Properties

| **Name** | **Type** | **Description** |
|:-------------|:----------------|-----------------|
|apiLambdaFunctions|`ApiLambdaFunction[]`|Returns an array of ApiLambdaFunction objects, where each has an `id` of the `apiIntegration` and the corresponding `lambda.Function` that it maps to.|
|apiGateway|[`api.SpecRestApi`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_apigateway.SpecRestApi.html)|Returns an instance of the API Gateway REST API created by the pattern.|
|apiGatewayCloudWatchRole?|[`iam.Role`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_iam.Role.html)|Returns an instance of the iam.Role created by the construct for API Gateway for CloudWatch access.|
|apiGatewayLogGroup|[`logs.LogGroup`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_logs.LogGroup.html)|Returns an instance of the LogGroup created by the construct for API Gateway access logging to CloudWatch.|

## Overview of how the OpenAPI file transformation works
This construct automatically transforms an incoming OpenAPI Definition (residing locally or in S3) by auto-populating the `uri` fields of the `x-amazon-apigateway-integration` integrations with the resolved value of the backing lambda functions. It does so by allowing the user to specify the `apiIntegrations` property and then correlates it with the api definition.

Looking at an example - a user creates an instantiation of `apiIntegrations` that specifies one integration named `MessagesHandler` that passes in a set of `lambda.FunctionProps` and a second integration named `PhotosHandler` that passes in an existing `lambda.Function`:

```typescript
const apiIntegrations: ApiIntegration[] = [
{
id: 'MessagesHandler',
lambdaFunctionProps: {
runtime: lambda.Runtime.NODEJS_18_X,
handler: 'index.handler',
code: lambda.Code.fromAsset(`${__dirname}/messages-lambda`),
}
},
{
id: 'PhotosHandler',
existingLambdaObj: new lambda.Function(this, 'PhotosLambda', {
runtime: lambda.Runtime.NODEJS_18_X,
handler: 'index.handler',
code: lambda.Code.fromAsset(`${__dirname}/photos-lambda`),
})
}
]
```

And a corresponding api definition with `GET` and `POST` methods on a `/messages` resource and a `GET` method on a `/photos` resource.

```
openapi: "3.0.1"
info:
title: "api"
version: "2023-02-20T20:46:08Z"
paths:
/messages:
get:
x-amazon-apigateway-integration:
httpMethod: "POST"
uri: MessagesHandler
passthroughBehavior: "when_no_match"
type: "aws_proxy"
post:
x-amazon-apigateway-integration:
httpMethod: "POST"
uri: MessagesHandler
passthroughBehavior: "when_no_match"
type: "aws_proxy"
/photos:
get:
x-amazon-apigateway-integration:
httpMethod: "POST"
uri: PhotosHandler
passthroughBehavior: "when_no_match"
type: "aws_proxy"
```

When the construct is created or updated, it will overwrite the `MessagesHandler` string with the fully resolved lambda proxy uri of the `MessagesHandlerLambdaFunction`, e.g., `arn:${Aws.PARTITION}:apigateway:${Aws.REGION}:lambda:path/2015-03-31/functions/${messagesLambda.functionArn}/invocations`, and similarly for the `PhotosHandler` string and `PhotosHandlerLambdaFunction`, resulting in a valid OpenAPI spec file that is then passed to the `SpecRestApi` construct.

For more information on specifying an API with OpenAPI, please see the [OpenAPI Specification](https://spec.openapis.org/oas/latest.html)

## ApiIntegration Details
This construct defines a custom type, `ApiIntegration`, that is specified as a required prop. The type has a required property, `id`, and two optional properties `existingLambdaObj` and `lambdaFunctionProps`. The `id` property is used to map the corresponding lambda function being defined with the placeholder string in the OpenAPI template file, and is not a CDK construct ID. Exactly one of `existingLambdaObj` or `lambdaFunctionProps` must be specified or the construct will throw an error.

## Default settings

Out of the box implementation of the Construct without any override will set the following defaults:

### Amazon API Gateway
* Deploy an edge-optimized API endpoint
* Enable CloudWatch logging for API Gateway
* Configure least privilege access IAM role for API Gateway
* Enable X-Ray Tracing

### AWS Lambda Function
* Configure limited privilege access IAM roles for Lambda functions
* Enable reusing connections with Keep-Alive for NodeJs Lambda functions
* Enable X-Ray Tracing
* Set Environment Variables
* AWS_NODEJS_CONNECTION_REUSE_ENABLED (for Node 10.x and higher functions)

## Architecture
![Architecture Diagram](architecture.png)

***
&copy; Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 09465d6

Please sign in to comment.