Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(new construct): aws-openapigateway-lambda #912

Merged
merged 61 commits into from
Jul 26, 2023
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
bf70adb
README for new openapi-based apigatway-to-lambda construct.
georgebearden Mar 1, 2023
824b57f
fix(core) prevent lambda id conflict with multiple buildLambdaFunctio…
alsoknownasdrew Mar 2, 2023
623aaba
chore(release): 2.33.0
aws-solutions-constructs-team Mar 3, 2023
ae3d91e
chore(release prep): Update CHANGELOG.md and align-version.js
biffgaut Mar 3, 2023
821a054
Freeze @types/node version in cdk-integ
biffgaut Mar 3, 2023
26e7c28
Freeze @types/node version in cdk-integ
biffgaut Mar 3, 2023
f26e8d4
Update step functions integ tests
biffgaut Mar 4, 2023
a8c1e44
chore(core): Add warnings about using core functions from outside of …
biffgaut Mar 9, 2023
e7eb012
fix(StepFunctions): Address LogGroup behavior problems (#922)
biffgaut Mar 17, 2023
4122462
Update package.json
biffgaut Mar 17, 2023
7e1ccfd
chore(release): 2.34.0
aws-solutions-constructs-team Mar 18, 2023
2de8284
chore(release-prep): Updated CHANGELOG.md and align-version.js
biffgaut Mar 18, 2023
720f4fd
chore(release-prep): align-version.js
biffgaut Mar 18, 2023
26a69f3
chore(core): migrate to assertions (#929)
biffgaut Mar 21, 2023
c909a16
Update the cloudfront-to-s3 construct to correctly set the logging bu…
georgebearden Mar 22, 2023
23ecefe
Update README
georgebearden Mar 23, 2023
b391da8
Merge branch 'main' into openapigateway-to-lambda
georgebearden Mar 23, 2023
93d05bd
Update openapigateway-to-lambda README
georgebearden Mar 24, 2023
a8312aa
Update README/architecture for openapigateway-to-lambda.
georgebearden Mar 24, 2023
5deb3ef
Add openapigateway-to-lambda code and initial tests
georgebearden Mar 24, 2023
7b3eb34
update openapigateway-to-lambda package.json dependency versions.
georgebearden Mar 24, 2023
0a622f7
Update openapigateway-to-lambda
georgebearden Mar 24, 2023
ffb71d1
Merge branch 'main' into openapigateway-to-lambda
georgebearden Mar 24, 2023
987b2fc
Update openapigateway README
georgebearden Mar 24, 2023
79caa76
don't depend on NodeJsFunction docker env
georgebearden Mar 24, 2023
6cff5b8
Update openapigateway-to-lambda README to reflect actual construct API.
georgebearden Mar 27, 2023
55c9dd4
temp commit
georgebearden Mar 28, 2023
fa7921d
Update snapshot test for openapigateway-to-lambda construct.
georgebearden Apr 4, 2023
52e5a0b
Merge branch 'main' into openapigateway-to-lambda
georgebearden Apr 4, 2023
ca2fc49
fix package.json version field
georgebearden Apr 4, 2023
88bdb00
update snapshot
georgebearden Apr 5, 2023
e93b071
update openapigateway-to-lambda custom resource to suppress standard …
georgebearden Apr 5, 2023
e46a15f
[wip] resources project
georgebearden May 8, 2023
f2749ee
fix dependency self reference on new resources module
georgebearden May 8, 2023
f09acc7
fix dependency self reference on new resources module
georgebearden May 8, 2023
02da2e1
Add integ test to the template writer resource.
georgebearden May 8, 2023
c7fb7cd
Add integ tests to template-writer resource.
georgebearden May 10, 2023
a260a8e
Merge branch 'main' into openapigateway-to-lambda
georgebearden May 10, 2023
56364de
Update template-writer resource integ tests to clean up test buckets …
georgebearden May 10, 2023
5cc0052
Add additinal tests to get 100% coverage on aws-openapigateway-lambda
georgebearden May 10, 2023
d2c0a09
Add additional integration tests to template resource writer and aws-…
georgebearden May 12, 2023
c600aac
remove eslintignore line that was obsolete
georgebearden May 12, 2023
4c33302
Merge branch 'main' into openapigateway-to-lambda
georgebearden May 12, 2023
b1d6b9d
cleanup eslint ignore and update openapigateway-to-lambda props.
georgebearden May 15, 2023
a4f42c5
Update python/java code samples for openapigateway-to-lambda.
georgebearden May 15, 2023
4d24c86
Update openapigateway-to-lambda README
georgebearden May 15, 2023
0027bec
Update resources README
georgebearden May 15, 2023
704ddd9
Update resources integ test
georgebearden May 15, 2023
0ff749a
update resources integ test snapshot
georgebearden May 16, 2023
75386c5
Update integ test snapshots for aws-openapigateway-lambda.
georgebearden May 16, 2023
252963c
Update aws-openapigateway-lambda construct to trigger api deployments…
georgebearden May 22, 2023
5721230
Add new integ test for cognito authorizer on aws-openapigateway-lambd…
georgebearden Jun 26, 2023
59d9774
address minor pr feedback.
georgebearden Jun 27, 2023
6e16923
Remove integration tests that use BucketDeployment as the asset hash …
georgebearden Jun 29, 2023
e74b89c
Remove integration tests that use BucketDeployment as the asset hash …
georgebearden Jun 29, 2023
2a91311
Address pr feedback.
georgebearden Jun 30, 2023
c2ba305
Add additional tests to aws-openapigateway-lambda construct.
georgebearden Jul 3, 2023
f742c92
Merge branch 'main' into openapigateway-to-lambda
georgebearden Jul 3, 2023
87a88eb
Update integ tests after latest cdk lib update.
georgebearden Jul 3, 2023
1ed9b84
Add optional construct id parameter to the buildLambdaFunction function.
georgebearden Jul 20, 2023
43cd699
Update aws-openapigateway-lambda property descriptions to better expl…
georgebearden Jul 26, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,13 @@ new OpenApiGatewayToLambda(this, "OpenApiGatewayToLambda", OpenApiGatewayToLambd
| **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 open-api spec file is located. When specifying this property, `apiDefinitionKey` must also be specified.|
|apiDefinitionKey?|`string`|S3 Object name of the open-api 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 open-api spec file.|
|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.|
|transformTimeout?|[`cdk.Duration`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.Duration.html)|Optional timeout for the backing lambda function that does the OpenAPI Definition transformation.|
|transformMemorySize?|[`number`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda.Function.html#memorysize)|Optional memory size for the backing lambda function that does the OpenAPI Definition transformation.|

## Pattern Properties

Expand Down Expand Up @@ -192,6 +194,11 @@ paths:

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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
*/

import { Aws } from 'aws-cdk-lib';
import * as cdk from "aws-cdk-lib";
import { Construct } from 'constructs';
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
import * as lambda from 'aws-cdk-lib/aws-lambda';
Expand All @@ -22,7 +23,6 @@ import * as defaults from '@aws-solutions-constructs/core';
import * as resources from '@aws-solutions-constructs/resources';
import { RestApiBaseProps } from 'aws-cdk-lib/aws-apigateway';
import { Asset } from 'aws-cdk-lib/aws-s3-assets';
import { overrideProps } from '@aws-solutions-constructs/core';

/**
* The ApiIntegration interface is used to correlate a user-specified id with either a existing lambda function or set of lambda props.
Expand All @@ -32,20 +32,26 @@ import { overrideProps } from '@aws-solutions-constructs/core';
export interface ApiIntegration {
/**
* Id of the ApiIntegration, used to correlate this lambda function to the api integration in the open api definition.
*
* Note this is not a CDK Construct ID, and is instead a string used to map the resolved lambda resource with the OpenAPI definition.
*/
readonly id: string;
/**
* Existing instance of Lambda Function object. Providing both this and `lambdaFunctionProps` will cause an error.
* The Lambda function to associate with the API method in the OpenAPI file matched by id.
*
* One and only one of existingLambdaObj or lambdaFunctionProps must be specified, any other combination will cause an error.
*/
readonly existingLambdaObj?: lambda.Function;
/**
* User provided props to override the default props for the Lambda function. Providing both this and `existingLambdaObj` will cause an error.
* The Lambda function to associate with the API method in the OpenAPI file matched by id.
*
* One and only one of existingLambdaObj or lambdaFunctionProps must be specified, any other combination will cause an error.
*/
readonly lambdaFunctionProps?: lambda.FunctionProps;
}

/**
* Helper object to map an ApiIntegration id to its resolved lambda.Function.
* Helper object to map an ApiIntegration id to its resolved lambda.Function. This type is exposed as a property on the instantiated construct.
*/
export interface ApiLambdaFunction {
/**
Expand All @@ -60,15 +66,15 @@ export interface ApiLambdaFunction {

export interface OpenApiGatewayToLambdaProps {
/**
* S3 Bucket where the open-api spec file is located. When specifying this property, apiDefinitionKey must also be specified.
* S3 Bucket where the OpenAPI spec file is located. When specifying this property, apiDefinitionKey must also be specified.
*/
readonly apiDefinitionBucket?: s3.IBucket;
/**
* S3 Object name of the open-api spec file. When specifying this property, apiDefinitionBucket must also be specified.
* S3 Object name of the OpenAPI spec file. When specifying this property, apiDefinitionBucket must also be specified.
*/
readonly apiDefinitionKey?: string;
/**
* Local file asset of the open-api spec file.
* Local file asset of the OpenAPI spec file.
*/
readonly apiDefinitionAsset?: Asset;
/**
Expand Down Expand Up @@ -108,6 +114,22 @@ export interface OpenApiGatewayToLambdaProps {
* @default - Default props are used
*/
readonly logGroupProps?: logs.LogGroupProps
/**
* Optional timeout for the backing lambda function that does the OpenAPI Definition transformation.
*
* Defaults to 3 seconds, but for larger files (tens/hundreds of megabytes or gigabytes in size) this value will need to be increased.
*
* @default Duration.seconds(3)
*/
readonly transformTimeout?: cdk.Duration;
/**
* Optional memory size for the backing lambda function that does the OpenAPI Definition transformation.
*
* Defaults to 128 MiB, but for larger files (tens/hundreds of megabytes or gigabytes in size) this value will need to be increased.
*
* @default 128
*/
readonly transformMemorySize?: number;
}

export class OpenApiGatewayToLambda extends Construct {
Expand All @@ -119,7 +141,7 @@ export class OpenApiGatewayToLambda extends Construct {
constructor(scope: Construct, id: string, props: OpenApiGatewayToLambdaProps) {
super(scope, id);

if (props.apiDefinitionBucket && props.apiDefinitionKey && props.apiDefinitionAsset) {
if (props.apiDefinitionAsset && (props.apiDefinitionBucket || props.apiDefinitionKey)) {
throw new Error('Either apiDefinitionBucket/apiDefinitionKey or apiDefinitionAsset must be specified, but not both');
}

Expand All @@ -139,45 +161,48 @@ export class OpenApiGatewayToLambda extends Construct {

this.apiLambdaFunctions = props.apiIntegrations.map(apiIntegration => {
lambdaCounter++;
if (apiIntegration.existingLambdaObj) {
return {
id: apiIntegration.id,
lambdaFunction: defaults.buildLambdaFunction(this, {
existingLambdaObj: apiIntegration.existingLambdaObj,
lambdaFunctionProps: apiIntegration.lambdaFunctionProps
})
};
} else if (apiIntegration.lambdaFunctionProps) {
// we need to pass unique name to the underlying buildLambdaFunction call to avoid naming collisions
let lambdaFunctionProps: lambda.FunctionProps = apiIntegration.lambdaFunctionProps;
if (lambdaFunctionProps?.functionName === undefined) {
lambdaFunctionProps = overrideProps(lambdaFunctionProps, {
functionName: defaults.generateName(this, `Function${lambdaCounter}`)
}, true);
}

return {
id: apiIntegration.id,
lambdaFunction: defaults.buildLambdaFunction(this, {
existingLambdaObj: apiIntegration.existingLambdaObj,
lambdaFunctionProps
})
};
} else {

if (apiIntegration.existingLambdaObj === undefined && apiIntegration.lambdaFunctionProps === undefined) {
throw new Error(`One of existingLambdaObj or lambdaFunctionProps must be specified for the api integration with id: ${apiIntegration.id}`);
}

// If we are creating the function and they did not specify a name, then we need to set a name
// to prevent a name collision
const defaultName = apiIntegration.lambdaFunctionProps ? defaults.generateName(this, `Function${lambdaCounter}`) : undefined;
const consolidatedLambdaProps = defaults.consolidateProps({ functionName: defaultName },
apiIntegration.lambdaFunctionProps);

return {
id: apiIntegration.id,
lambdaFunction: defaults.buildLambdaFunction(this, {
existingLambdaObj: apiIntegration.existingLambdaObj,
lambdaFunctionProps: consolidatedLambdaProps
})
};
});

// Map each id and lambda function pair to the required format for the template writer custom resource
const apiIntegrationUris = this.apiLambdaFunctions.map(apiLambdaFunction => {
// the placeholder string that will be replaced in the OpenAPI Definition
const uriPlaceholderString = apiLambdaFunction.id;
// the endpoint URI of the backing lambda function, as defined in the API Gateway extensions for OpenAPI here:
// https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-swagger-extensions-integration.html
const uriResolvedValue = `arn:${Aws.PARTITION}:apigateway:${Aws.REGION}:lambda:path/2015-03-31/functions/${apiLambdaFunction.lambdaFunction.functionArn}/invocations`;

return {
id: apiLambdaFunction.id,
value: `arn:${Aws.PARTITION}:apigateway:${Aws.REGION}:lambda:path/2015-03-31/functions/${apiLambdaFunction.lambdaFunction.functionArn}/invocations`
id: uriPlaceholderString,
value: uriResolvedValue
};
});

// This custom resource will overwrite the string placeholders in the openapi definition with the resolved values of the lambda URIs
const apiDefinitionWriter = resources.createTemplateWriterCustomResource(this, apiDefinitionBucket, apiDefinitionKey, apiIntegrationUris);
const apiDefinitionWriter = resources.createTemplateWriterCustomResource(this, 'Api', {
templateBucket: apiDefinitionBucket,
templateKey: apiDefinitionKey,
templateValues: apiIntegrationUris,
timeout: props.transformTimeout,
memorySize: props.transformMemorySize
});

const specRestApiResponse = defaults.CreateSpecRestApi(this, {
...props.apiGatewayProps,
Expand All @@ -194,8 +219,10 @@ export class OpenApiGatewayToLambda extends Construct {
// Redeploy the API any time the incoming API definition changes (from asset or s3 object)
this.apiGateway.latestDeployment?.addToLogicalId(apiDefinitionKey);

// Grant APIGW invocation rights of the backing lambda functions
this.apiLambdaFunctions.map(apiLambdaFunction => {
this.apiLambdaFunctions.forEach(apiLambdaFunction => {
// Redeploy the API any time one of the lambda functions changes
this.apiGateway.latestDeployment?.addToLogicalId(apiLambdaFunction.lambdaFunction.functionArn);
// Grant APIGW invocation rights for each lambda function
apiLambdaFunction.lambdaFunction.addPermission('PermitAPIGInvocation', {
principal: new iam.ServicePrincipal('apigateway.amazonaws.com'),
sourceArn: this.apiGateway.arnForExecuteApi('*')
Expand Down
Loading