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 <[email protected]>
Co-authored-by: AWS Solutions Constructs Automation <[email protected]>
Co-authored-by: biffgaut <[email protected]>
Co-authored-by: biffgaut <[email protected]>
  • Loading branch information
5 people authored 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.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 09465d6

Please sign in to comment.