From 68f807aa81b22d87e3df0c6b329aaa17cd628685 Mon Sep 17 00:00:00 2001 From: biffgaut Date: Tue, 3 Sep 2024 17:12:44 -0400 Subject: [PATCH 1/6] New Security.md page --- SECURITY.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..1ff69c49e --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,3 @@ +### Reporting Security Issues +We take all security reports seriously. When we receive such reports, we will investigate and subsequently address any potential vulnerabilities as quickly as possible. If you discover a potential security issue in this project, please notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/) or directly via email to [AWS Security](mailto:aws-security@amazon.com). Please do not create a public GitHub issue in this project. + From dcf9f1637682ad3d71bb4625680fbf96fe361917 Mon Sep 17 00:00:00 2001 From: biffgaut Date: Tue, 3 Sep 2024 23:56:23 -0400 Subject: [PATCH 2/6] Ignore security email address --- .viperlightignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.viperlightignore b/.viperlightignore index 430bfd610..d94f05b3b 100644 --- a/.viperlightignore +++ b/.viperlightignore @@ -11,6 +11,7 @@ source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.c source/patterns/@aws-solutions-constructs/aws-apigateway-dynamodb/test/integ.apiddb-apigateway-dynamodb-existing-table.expected.json:60 CODE_OF_CONDUCT.md:4 CONTRIBUTING.md:244 +SECURITY.md:2 source/patterns/@aws-solutions-constructs/core/test/step-function-helper.test.ts:107 source/patterns/@aws-solutions-constructs/aws-kinesisstreams-gluejob/test/test.kinesisstream-gluejob.test.ts:126 source/patterns/@aws-solutions-constructs/aws-lambda-sqs/test/integ.lamsqs-deployFunction.expected.json:112 From e5b514fab0586a9c65df48f97d860fa905ae2e50 Mon Sep 17 00:00:00 2001 From: biffgaut <78155736+biffgaut@users.noreply.github.com> Date: Fri, 6 Sep 2024 14:49:33 -0400 Subject: [PATCH 3/6] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14da2da4f..95342227a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -218,7 +218,7 @@ Built on CDK v2.111.0 ### Bug Fixes * **aws-apigateway-sqs:** Remove /message path when delete method is not allowed ([#1030](https://github.com/awslabs/aws-solutions-constructs/issues/1030)) ([f772200](https://github.com/awslabs/aws-solutions-constructs/commit/f772200d6885cf0e0030239ce6f7511cdb2814d6)) -======= + ## [2.47.0](https://github.com/aws-solutions-constructs-team/aws-solutions-constructs-test/compare/v2.14.0...v2.47.0) (2023-11-27) From b1baf59c13d6270cd1e32dcaed7dae3c6b302087 Mon Sep 17 00:00:00 2001 From: Jonathan Barnett Date: Wed, 11 Sep 2024 12:07:54 +1000 Subject: [PATCH 4/6] feat(aws-openapigateway-lambda): update construct to allow specifying an inline api definition (#1190) * feat(aws-openapigateway-lambda): update construct to allow specifying an inline api definition The rationale for this change is to improve the local developer experience and enable compatibility with `sam local start-api`. Previously, the construct only supported referencing an external OpenAPI definition file, which made it challenging to work with an API locally during development. * fix: amend typo in comment * Constructs Team Updates * Final edits (new tests, var name changes) * Sync README * Change name back * Comment on the export * Change name to apiDefinitionJson * Address comments * trailing whitespace * Build tweaks --------- Co-authored-by: Jonathan Barnett Co-authored-by: biffgaut Co-authored-by: AWS Solutions Constructs Team <67720492+aws-solutions-constructs-team@users.noreply.github.com> --- .../aws-openapigateway-lambda/README.md | 1 + .../aws-openapigateway-lambda/lib/index.ts | 127 +- .../lib/openapi-helper.ts | 185 +++ .../index.mjs | 16 + .../index.mjs | 16 + .../cdk.out | 1 + .../integ.json | 12 + .../manifest.json | 203 ++++ ...opilam-apiFromInlineDefinition.assets.json | 45 + ...ilam-apiFromInlineDefinition.template.json | 773 +++++++++++++ ...efaultTestDeployAssertCF0E111E.assets.json | 19 + ...aultTestDeployAssertCF0E111E.template.json | 36 + .../tree.json | 1029 +++++++++++++++++ .../integ.opilam-apiFromInlineDefinition.ts | 51 + .../test/openapi/apiDefinition.json | 57 + .../test/test.openapigateway-lambda.test.ts | 437 +++++-- 16 files changed, 2802 insertions(+), 206 deletions(-) create mode 100644 source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/lib/openapi-helper.ts create mode 100644 source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/asset.654d49d4ea47a6be417d57b94dc0310933d0e971a3e48a3080c3e48487af3e50/index.mjs create mode 100644 source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/asset.8ce85d10dcd7b8e6d43ffd909827afc76802ad40fcd7908886ff825cbe8e15df/index.mjs create mode 100644 source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/cdk.out create mode 100644 source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/integ.json create mode 100644 source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/manifest.json create mode 100644 source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/opilam-apiFromInlineDefinition.assets.json create mode 100644 source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/opilam-apiFromInlineDefinition.template.json create mode 100644 source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/opilamapiFromInlineDefinitionIntegDefaultTestDeployAssertCF0E111E.assets.json create mode 100644 source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/opilamapiFromInlineDefinitionIntegDefaultTestDeployAssertCF0E111E.template.json create mode 100644 source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/tree.json create mode 100644 source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.ts create mode 100644 source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/openapi/apiDefinition.json diff --git a/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/README.md b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/README.md index 919e93d00..8d8b4a8d3 100755 --- a/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/README.md +++ b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/README.md @@ -126,6 +126,7 @@ new OpenApiGatewayToLambda(this, "OpenApiGatewayToLambda", OpenApiGatewayToLambd |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.| +|apiDefinitionJson?|any|OpenAPI specification represented in a JSON object to be embedded in the CloudFormation template. IMPORTANT - Including the spec in the template introduces a risk of the template growing too big, but there are some use cases that require an embedded spec. Unless your use case explicitly requires an embedded spec you should pass your spec as an S3 asset.| |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.| diff --git a/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/lib/index.ts b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/lib/index.ts index a7e48b934..4ee0e2224 100644 --- a/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/lib/index.ts +++ b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/lib/index.ts @@ -11,58 +11,18 @@ * and limitations under the License. */ -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'; import * as logs from 'aws-cdk-lib/aws-logs'; import * as iam from 'aws-cdk-lib/aws-iam'; import * as s3 from 'aws-cdk-lib/aws-s3'; 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'; - -/** - * The ApiIntegration interface is used to correlate a user-specified id with either a existing lambda function or set of lambda props. - * - * See the 'Overview of how the OpenAPI file transformation works' section of the README.md for more details on its usage. - */ -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 client defined string used to map the resolved lambda resource with the OpenAPI definition. - */ - readonly id: string; - /** - * 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; - /** - * Properties for the Lambda function to create and 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. This type is exposed as a property on the instantiated construct. - */ -export interface ApiLambdaFunction { - /** - * Id of the ApiIntegration, used to correlate this lambda function to the api integration in the open api definition. - */ - readonly id: string; - /** - * The instantiated lambda.Function. - */ - readonly lambdaFunction: lambda.Function; -} +import { ApiIntegration, CheckOpenApiProps, ApiLambdaFunction, ObtainApiDefinition } from './openapi-helper'; +// openapi-helper is on its way to core, so these interfaces must be exported here +export { ApiIntegration, ApiLambdaFunction } from './openapi-helper'; export interface OpenApiGatewayToLambdaProps { /** @@ -77,6 +37,13 @@ export interface OpenApiGatewayToLambdaProps { * Local file asset of the OpenAPI spec file. */ readonly apiDefinitionAsset?: Asset; + /** + * OpenAPI specification represented in a JSON object to be embedded in the CloudFormation template. + * IMPORTANT - Including the spec in the template introduces a risk of the template growing too big, but + * there are some use cases that require an embedded spec. Unless your use case explicitly requires an embedded spec + * you should pass your spec as an S3 asset. + */ + readonly apiDefinitionJson?: any; /** * 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. @@ -144,10 +111,7 @@ export class OpenApiGatewayToLambda extends Construct { constructor(scope: Construct, id: string, props: OpenApiGatewayToLambdaProps) { super(scope, id); - CheckOpenapiProps(props); - - const apiDefinitionBucket = props.apiDefinitionBucket ?? props.apiDefinitionAsset?.bucket; - const apiDefinitionKey = props.apiDefinitionKey ?? props.apiDefinitionAsset?.s3ObjectKey; + CheckOpenApiProps(props); // store a counter to be able to uniquely name lambda functions to avoid naming collisions let lambdaCounter = 0; @@ -169,45 +133,27 @@ export class OpenApiGatewayToLambda extends Construct { } }); - // 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: 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, 'Api', { - // CheckAlbProps() has confirmed the existence of these values - templateBucket: apiDefinitionBucket!, - templateKey: apiDefinitionKey!, - templateValues: apiIntegrationUris, - timeout: props.internalTransformTimeout ?? cdk.Duration.minutes(1), - memorySize: props.internalTransformMemorySize ?? 1024 + const definition = ObtainApiDefinition(this, { + tokenToFunctionMap: this.apiLambdaFunctions, + apiDefinitionBucket: props.apiDefinitionBucket, + apiDefinitionKey: props.apiDefinitionKey, + apiDefinitionAsset: props.apiDefinitionAsset, + apiJsonDefinition: props.apiDefinitionJson, + internalTransformTimeout: props.internalTransformTimeout, + internalTransformMemorySize: props.internalTransformMemorySize }); const specRestApiResponse = defaults.CreateSpecRestApi(this, { ...props.apiGatewayProps, - apiDefinition: apigateway.ApiDefinition.fromBucket( - apiDefinitionWriter.s3Bucket, - apiDefinitionWriter.s3Key - ) + apiDefinition: definition }, props.logGroupProps); this.apiGateway = specRestApiResponse.api; this.apiGatewayCloudWatchRole = specRestApiResponse.role; this.apiGatewayLogGroup = specRestApiResponse.logGroup; - // Redeploy the API any time the incoming API definition changes (from asset or s3 object) - this.apiGateway.latestDeployment?.addToLogicalId(apiDefinitionKey); - + // Redeploy the API any time a decoupled (non-inline) API definition changes (from asset or s3 object) + this.apiGateway.latestDeployment?.addToLogicalId(props.apiDefinitionKey ?? props.apiDefinitionAsset?.s3ObjectKey); this.apiLambdaFunctions.forEach(apiLambdaFunction => { // Redeploy the API any time one of the lambda functions changes this.apiGateway.latestDeployment?.addToLogicalId(apiLambdaFunction.lambdaFunction.functionArn); @@ -219,32 +165,3 @@ export class OpenApiGatewayToLambda extends Construct { }); } } - -function CheckOpenapiProps(props: OpenApiGatewayToLambdaProps) { - - let errorMessages = ''; - let errorFound = false; - - if (props.apiDefinitionAsset && (props.apiDefinitionBucket || props.apiDefinitionKey)) { - errorMessages += 'Either apiDefinitionBucket/apiDefinitionKey or apiDefinitionAsset must be specified, but not both\n'; - errorFound = true; - } - - const apiDefinitionBucket = props.apiDefinitionBucket ?? props.apiDefinitionAsset?.bucket; - const apiDefinitionKey = props.apiDefinitionKey ?? props.apiDefinitionAsset?.s3ObjectKey; - - if (apiDefinitionBucket === undefined || apiDefinitionKey === undefined) { - errorMessages += 'Either apiDefinitionBucket/apiDefinitionKey or apiDefinitionAsset must be specified\n'; - errorFound = true; - } - - if (props.apiIntegrations === undefined || props.apiIntegrations.length < 1) { - errorMessages += 'At least one ApiIntegration must be specified in the apiIntegrations property\n'; - errorFound = true; - } - - if (errorFound) { - throw new Error(errorMessages); - } - -} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/lib/openapi-helper.ts b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/lib/openapi-helper.ts new file mode 100644 index 000000000..fea66eb67 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/lib/openapi-helper.ts @@ -0,0 +1,185 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +/* + * The functions found here in the core library are for internal use and can be changed + * or removed outside of a major release. We recommend against calling them directly from client code. + */ + +/* + * This file is core openapi functionality and should ideally be in the core library. Since + * that causes a circular reference with the resources library we have left it here for now + * in the interest of getting these updates out faster + */ + +import * as apigateway from 'aws-cdk-lib/aws-apigateway'; +import * as lambda from 'aws-cdk-lib/aws-lambda'; +import * as s3 from 'aws-cdk-lib/aws-s3'; +import { Asset } from 'aws-cdk-lib/aws-s3-assets'; +import { Aws, Duration } from "aws-cdk-lib"; +import { Construct } from 'constructs'; +import * as resources from '@aws-solutions-constructs/resources'; + +/** + * The ApiIntegration interface is used to correlate a user-specified id with either a existing lambda function or set of lambda props. + * + * See the 'Overview of how the OpenAPI file transformation works' section of the README.md for more details on its usage. + */ +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 client defined string used to map the resolved lambda resource with the OpenAPI definition. + */ + readonly id: string; + /** + * 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; + /** + * Properties for the Lambda function to create and 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. This type is exposed as a property on the instantiated construct. + */ +export interface ApiLambdaFunction { + /** + * Id of the ApiIntegration, used to correlate this lambda function to the api integration in the open api definition. + */ + readonly id: string; + /** + * The instantiated lambda.Function. + */ + readonly lambdaFunction: lambda.Function; +} + +export interface OpenApiProps { + readonly apiDefinitionAsset?: Asset, + readonly apiDefinitionJson?: any, + readonly apiDefinitionBucket?: s3.IBucket, + readonly apiDefinitionKey?: string, + readonly apiIntegrations: ApiIntegration[] +} + +/** + * @internal This is an internal core function and should not be called directly by Solutions Constructs clients. + */ +export function CheckOpenApiProps(props: OpenApiProps) { + + let errorMessages = ''; + let errorFound = false; + + if ((props.apiDefinitionBucket && !props.apiDefinitionKey) || (!props.apiDefinitionBucket && props.apiDefinitionKey)) { + errorMessages += 'apiDefinitionBucket and apiDefinitionKey must be specified together.\n'; + errorFound = true; + } + + const definitionCount: number = + (props.apiDefinitionAsset ? 1 : 0) + + (props.apiDefinitionBucket ? 1 : 0) + + (props.apiDefinitionJson ? 1 : 0); + + if (definitionCount !== 1) { + errorMessages += 'Exactly one of apiDefinitionAsset, apiDefinitionJson or (apiDefinitionBucket/apiDefinitionKey) must be provided\n'; + errorFound = true; + } + + if (props.apiIntegrations === undefined || props.apiIntegrations.length < 1) { + errorMessages += 'At least one ApiIntegration must be specified in the apiIntegrations property\n'; + errorFound = true; + } + + if (errorFound) { + throw new Error(errorMessages); + } + +} + +export interface ObtainApiDefinitionProps { + readonly tokenToFunctionMap: ApiLambdaFunction[], + readonly apiDefinitionBucket?: s3.IBucket, + readonly apiDefinitionKey?: string, + readonly apiDefinitionAsset?: Asset, + readonly apiJsonDefinition?: any, + readonly internalTransformTimeout?: Duration, + readonly internalTransformMemorySize?: number +} + +/** + * @internal This is an internal core function and should not be called directly by Solutions Constructs clients. + */ +export function ObtainApiDefinition(scope: Construct, props: ObtainApiDefinitionProps): apigateway.ApiDefinition { + const apiRawInlineSpec = props.apiJsonDefinition; + const meldedDefinitionBucket = props.apiDefinitionBucket ?? props.apiDefinitionAsset?.bucket; + const meldedDefinitionKey = props.apiDefinitionKey ?? props.apiDefinitionAsset?.s3ObjectKey; + + // Map each id and lambda function pair to the required format for the template writer custom resource + const apiIntegrationUris = props.tokenToFunctionMap.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: uriPlaceholderString, + value: uriResolvedValue + }; + }); + + let apiDefinitionWriter: resources.CreateTemplateWriterResponse | undefined; + let newApiDefinition: apigateway.ApiDefinition | undefined; + + if (props.apiDefinitionAsset || props.apiDefinitionBucket) { + // This custom resource will overwrite the string placeholders in the openapi definition with the resolved values of the lambda URIs + apiDefinitionWriter = resources.createTemplateWriterCustomResource(scope, 'Api', { + // CheckOpenapiProps() has confirmed the existence of these values + templateBucket: meldedDefinitionBucket!, + templateKey: meldedDefinitionKey!, + templateValues: apiIntegrationUris, + timeout: props.internalTransformTimeout ?? Duration.minutes(1), + memorySize: props.internalTransformMemorySize ?? 1024 + }); + + newApiDefinition = apigateway.ApiDefinition.fromBucket( + apiDefinitionWriter.s3Bucket, + apiDefinitionWriter.s3Key + ); + } else if (apiRawInlineSpec) { + const apiInlineSpec = new apigateway.InlineApiDefinition(apiRawInlineSpec); + newApiDefinition = InlineTemplateWriter(apiInlineSpec.bind(scope), apiIntegrationUris); + } else { + throw new Error("No definition provided (this code should be unreachable)"); + } + + return newApiDefinition!; +} + +function InlineTemplateWriter({ inlineDefinition }: apigateway.ApiDefinitionConfig, templateValues: resources.TemplateValue[]) { + let template = JSON.stringify(inlineDefinition); + + // This replicates logic in the template writer custom resource (resources/lib/template-writer-custom-resource/index.ts), + // any logic changes should be made to both locations every time + templateValues.forEach((templateValue) => { + template = template?.replace(new RegExp(templateValue.id, 'g'), templateValue.value); + }); + + return new apigateway.InlineApiDefinition(JSON.parse(template)); +} diff --git a/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/asset.654d49d4ea47a6be417d57b94dc0310933d0e971a3e48a3080c3e48487af3e50/index.mjs b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/asset.654d49d4ea47a6be417d57b94dc0310933d0e971a3e48a3080c3e48487af3e50/index.mjs new file mode 100644 index 000000000..220496109 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/asset.654d49d4ea47a6be417d57b94dc0310933d0e971a3e48a3080c3e48487af3e50/index.mjs @@ -0,0 +1,16 @@ +export const handler = async (event) => { + switch (event.httpMethod) { + case 'POST': + return { + statusCode: 200, + body: JSON.stringify({"message": "successfully handled POST from photos lambda"}) + }; + case 'GET': + return { + statusCode: 200, + body: JSON.stringify({"message": "successfully handled GET from photos lambda"}) + }; + default: + throw new Error(`cannot handle httpMethod: ${event.httpMethod}`); + } +}; diff --git a/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/asset.8ce85d10dcd7b8e6d43ffd909827afc76802ad40fcd7908886ff825cbe8e15df/index.mjs b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/asset.8ce85d10dcd7b8e6d43ffd909827afc76802ad40fcd7908886ff825cbe8e15df/index.mjs new file mode 100644 index 000000000..dfcaf1052 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/asset.8ce85d10dcd7b8e6d43ffd909827afc76802ad40fcd7908886ff825cbe8e15df/index.mjs @@ -0,0 +1,16 @@ +export const handler = async (event) => { + switch (event.httpMethod) { + case 'POST': + return { + statusCode: 200, + body: JSON.stringify({"message": "successfully handled POST from messages lambda"}) + }; + case 'GET': + return { + statusCode: 200, + body: JSON.stringify({"message": "successfully handled GET from messages lambda"}) + }; + default: + throw new Error(`cannot handle httpMethod: ${event.httpMethod}`); + } +}; diff --git a/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/cdk.out b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/cdk.out new file mode 100644 index 000000000..e033e8360 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"36.0.20"} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/integ.json b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/integ.json new file mode 100644 index 000000000..1817a76af --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "36.0.20", + "testCases": { + "opilam-apiFromInlineDefinition/Integ/DefaultTest": { + "stacks": [ + "opilam-apiFromInlineDefinition" + ], + "assertionStack": "opilam-apiFromInlineDefinition/Integ/DefaultTest/DeployAssert", + "assertionStackName": "opilamapiFromInlineDefinitionIntegDefaultTestDeployAssertCF0E111E" + } + } +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/manifest.json b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/manifest.json new file mode 100644 index 000000000..79ac87f56 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/manifest.json @@ -0,0 +1,203 @@ +{ + "version": "36.0.5", + "artifacts": { + "opilamapiFromInlineDefinitionIntegDefaultTestDeployAssertCF0E111E.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "opilamapiFromInlineDefinitionIntegDefaultTestDeployAssertCF0E111E.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "opilamapiFromInlineDefinitionIntegDefaultTestDeployAssertCF0E111E": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "opilamapiFromInlineDefinitionIntegDefaultTestDeployAssertCF0E111E.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "opilamapiFromInlineDefinitionIntegDefaultTestDeployAssertCF0E111E.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "opilamapiFromInlineDefinitionIntegDefaultTestDeployAssertCF0E111E.assets" + ], + "metadata": { + "/opilam-apiFromInlineDefinition/Integ/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/opilam-apiFromInlineDefinition/Integ/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "opilam-apiFromInlineDefinition/Integ/DefaultTest/DeployAssert" + }, + "opilam-apiFromInlineDefinition.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "opilam-apiFromInlineDefinition.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "opilam-apiFromInlineDefinition": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "opilam-apiFromInlineDefinition.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/97d284396586d88e440dcc04bdaa6eda5c6fea4dd2ba30fc55c76f9af72c20b8.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "opilam-apiFromInlineDefinition.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "opilam-apiFromInlineDefinition.assets" + ], + "metadata": { + "/opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/MessagesHandlerApiFunction0ServiceRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "OpenApiGatewayToLambdaMessagesHandlerApiFunction0ServiceRoleB5DC63A9" + } + ], + "/opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/MessagesHandlerApiFunction0ServiceRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "OpenApiGatewayToLambdaMessagesHandlerApiFunction0ServiceRoleDefaultPolicy4563083B" + } + ], + "/opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/MessagesHandlerApiFunction0/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "OpenApiGatewayToLambdaMessagesHandlerApiFunction0E78C5307" + } + ], + "/opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/MessagesHandlerApiFunction0/PermitAPIGInvocation": [ + { + "type": "aws:cdk:logicalId", + "data": "OpenApiGatewayToLambdaMessagesHandlerApiFunction0PermitAPIGInvocationB70BC520" + } + ], + "/opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/PhotosHandlerApiFunction1ServiceRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "OpenApiGatewayToLambdaPhotosHandlerApiFunction1ServiceRoleA572C749" + } + ], + "/opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/PhotosHandlerApiFunction1ServiceRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "OpenApiGatewayToLambdaPhotosHandlerApiFunction1ServiceRoleDefaultPolicy389AA3BA" + } + ], + "/opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/PhotosHandlerApiFunction1/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "OpenApiGatewayToLambdaPhotosHandlerApiFunction1C42C52E8" + } + ], + "/opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/PhotosHandlerApiFunction1/PermitAPIGInvocation": [ + { + "type": "aws:cdk:logicalId", + "data": "OpenApiGatewayToLambdaPhotosHandlerApiFunction1PermitAPIGInvocationA20E40FB" + } + ], + "/opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/ApiAccessLogGroup/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "OpenApiGatewayToLambdaApiAccessLogGroupE21284EC" + } + ], + "/opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/SpecRestApi/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "OpenApiGatewayToLambdaSpecRestApi27C18064" + } + ], + "/opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/SpecRestApi/Deployment/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "OpenApiGatewayToLambdaSpecRestApiDeploymentC8BD07754bd94750334d61fd5d4bd8fcdf375863" + } + ], + "/opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/SpecRestApi/DeploymentStage.prod/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "OpenApiGatewayToLambdaSpecRestApiDeploymentStageprodB962CBBB" + } + ], + "/opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/SpecRestApi/Endpoint": [ + { + "type": "aws:cdk:logicalId", + "data": "OpenApiGatewayToLambdaSpecRestApiEndpointD1FA5E3A" + } + ], + "/opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/SpecRestApi/UsagePlan/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "OpenApiGatewayToLambdaSpecRestApiUsagePlan44F6F5C6" + } + ], + "/opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/LambdaRestApiCloudWatchRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "OpenApiGatewayToLambdaLambdaRestApiCloudWatchRoleBC7E6D95" + } + ], + "/opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/LambdaRestApiAccount": [ + { + "type": "aws:cdk:logicalId", + "data": "OpenApiGatewayToLambdaLambdaRestApiAccount71EC7DA6" + } + ], + "/opilam-apiFromInlineDefinition/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/opilam-apiFromInlineDefinition/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "opilam-apiFromInlineDefinition" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/opilam-apiFromInlineDefinition.assets.json b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/opilam-apiFromInlineDefinition.assets.json new file mode 100644 index 000000000..960504173 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/opilam-apiFromInlineDefinition.assets.json @@ -0,0 +1,45 @@ +{ + "version": "36.0.20", + "files": { + "8ce85d10dcd7b8e6d43ffd909827afc76802ad40fcd7908886ff825cbe8e15df": { + "source": { + "path": "asset.8ce85d10dcd7b8e6d43ffd909827afc76802ad40fcd7908886ff825cbe8e15df", + "packaging": "zip" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "8ce85d10dcd7b8e6d43ffd909827afc76802ad40fcd7908886ff825cbe8e15df.zip", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + }, + "654d49d4ea47a6be417d57b94dc0310933d0e971a3e48a3080c3e48487af3e50": { + "source": { + "path": "asset.654d49d4ea47a6be417d57b94dc0310933d0e971a3e48a3080c3e48487af3e50", + "packaging": "zip" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "654d49d4ea47a6be417d57b94dc0310933d0e971a3e48a3080c3e48487af3e50.zip", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + }, + "97d284396586d88e440dcc04bdaa6eda5c6fea4dd2ba30fc55c76f9af72c20b8": { + "source": { + "path": "opilam-apiFromInlineDefinition.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "97d284396586d88e440dcc04bdaa6eda5c6fea4dd2ba30fc55c76f9af72c20b8.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/opilam-apiFromInlineDefinition.template.json b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/opilam-apiFromInlineDefinition.template.json new file mode 100644 index 000000000..b8c350d41 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/opilam-apiFromInlineDefinition.template.json @@ -0,0 +1,773 @@ +{ + "Description": "Integration Test for aws-openapigateway-lambda", + "Resources": { + "OpenApiGatewayToLambdaMessagesHandlerApiFunction0ServiceRoleB5DC63A9": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/lambda/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "LambdaFunctionServiceRolePolicy" + } + ] + }, + "Metadata": { + "guard": { + "SuppressedRules": [ + "IAM_NO_INLINE_POLICY_CHECK" + ] + } + } + }, + "OpenApiGatewayToLambdaMessagesHandlerApiFunction0ServiceRoleDefaultPolicy4563083B": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "xray:PutTelemetryRecords", + "xray:PutTraceSegments" + ], + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "OpenApiGatewayToLambdaMessagesHandlerApiFunction0ServiceRoleDefaultPolicy4563083B", + "Roles": [ + { + "Ref": "OpenApiGatewayToLambdaMessagesHandlerApiFunction0ServiceRoleB5DC63A9" + } + ] + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W12", + "reason": "Lambda needs the following minimum required permissions to send trace data to X-Ray and access ENIs in a VPC." + } + ] + } + } + }, + "OpenApiGatewayToLambdaMessagesHandlerApiFunction0E78C5307": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "8ce85d10dcd7b8e6d43ffd909827afc76802ad40fcd7908886ff825cbe8e15df.zip" + }, + "Environment": { + "Variables": { + "AWS_NODEJS_CONNECTION_REUSE_ENABLED": "1" + } + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "OpenApiGatewayToLambdaMessagesHandlerApiFunction0ServiceRoleB5DC63A9", + "Arn" + ] + }, + "Runtime": "nodejs20.x", + "TracingConfig": { + "Mode": "Active" + } + }, + "DependsOn": [ + "OpenApiGatewayToLambdaMessagesHandlerApiFunction0ServiceRoleDefaultPolicy4563083B", + "OpenApiGatewayToLambdaMessagesHandlerApiFunction0ServiceRoleB5DC63A9" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W58", + "reason": "Lambda functions has the required permission to write CloudWatch Logs. It uses custom policy instead of arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole with tighter permissions." + }, + { + "id": "W89", + "reason": "This is not a rule for the general case, just for specific use cases/industries" + }, + { + "id": "W92", + "reason": "Impossible for us to define the correct concurrency for clients" + } + ] + } + } + }, + "OpenApiGatewayToLambdaMessagesHandlerApiFunction0PermitAPIGInvocationB70BC520": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "OpenApiGatewayToLambdaMessagesHandlerApiFunction0E78C5307", + "Arn" + ] + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":execute-api:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + { + "Ref": "OpenApiGatewayToLambdaSpecRestApi27C18064" + }, + "/*/*/*" + ] + ] + } + } + }, + "OpenApiGatewayToLambdaPhotosHandlerApiFunction1ServiceRoleA572C749": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/lambda/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "LambdaFunctionServiceRolePolicy" + } + ] + }, + "Metadata": { + "guard": { + "SuppressedRules": [ + "IAM_NO_INLINE_POLICY_CHECK" + ] + } + } + }, + "OpenApiGatewayToLambdaPhotosHandlerApiFunction1ServiceRoleDefaultPolicy389AA3BA": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "xray:PutTelemetryRecords", + "xray:PutTraceSegments" + ], + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "OpenApiGatewayToLambdaPhotosHandlerApiFunction1ServiceRoleDefaultPolicy389AA3BA", + "Roles": [ + { + "Ref": "OpenApiGatewayToLambdaPhotosHandlerApiFunction1ServiceRoleA572C749" + } + ] + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W12", + "reason": "Lambda needs the following minimum required permissions to send trace data to X-Ray and access ENIs in a VPC." + } + ] + } + } + }, + "OpenApiGatewayToLambdaPhotosHandlerApiFunction1C42C52E8": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "654d49d4ea47a6be417d57b94dc0310933d0e971a3e48a3080c3e48487af3e50.zip" + }, + "Environment": { + "Variables": { + "AWS_NODEJS_CONNECTION_REUSE_ENABLED": "1" + } + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "OpenApiGatewayToLambdaPhotosHandlerApiFunction1ServiceRoleA572C749", + "Arn" + ] + }, + "Runtime": "nodejs20.x", + "TracingConfig": { + "Mode": "Active" + } + }, + "DependsOn": [ + "OpenApiGatewayToLambdaPhotosHandlerApiFunction1ServiceRoleDefaultPolicy389AA3BA", + "OpenApiGatewayToLambdaPhotosHandlerApiFunction1ServiceRoleA572C749" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W58", + "reason": "Lambda functions has the required permission to write CloudWatch Logs. It uses custom policy instead of arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole with tighter permissions." + }, + { + "id": "W89", + "reason": "This is not a rule for the general case, just for specific use cases/industries" + }, + { + "id": "W92", + "reason": "Impossible for us to define the correct concurrency for clients" + } + ] + } + } + }, + "OpenApiGatewayToLambdaPhotosHandlerApiFunction1PermitAPIGInvocationA20E40FB": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "OpenApiGatewayToLambdaPhotosHandlerApiFunction1C42C52E8", + "Arn" + ] + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":execute-api:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + { + "Ref": "OpenApiGatewayToLambdaSpecRestApi27C18064" + }, + "/*/*/*" + ] + ] + } + } + }, + "OpenApiGatewayToLambdaApiAccessLogGroupE21284EC": { + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain", + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W86", + "reason": "Retention period for CloudWatchLogs LogGroups are set to 'Never Expire' to preserve customer data indefinitely" + }, + { + "id": "W84", + "reason": "By default CloudWatchLogs LogGroups data is encrypted using the CloudWatch server-side encryption keys (AWS Managed Keys)" + } + ] + } + } + }, + "OpenApiGatewayToLambdaSpecRestApi27C18064": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "openapi": "3.0.1", + "info": { + "title": "api", + "version": "2023-02-20T20:46:08Z" + }, + "paths": { + "/messages": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "uri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":apigateway:", + { + "Ref": "AWS::Region" + }, + ":lambda:path/2015-03-31/functions/", + { + "Fn::GetAtt": [ + "OpenApiGatewayToLambdaMessagesHandlerApiFunction0E78C5307", + "Arn" + ] + }, + "/invocations" + ] + ] + }, + "passthroughBehavior": "when_no_match", + "type": "aws_proxy" + }, + "x-amazon-apigateway-auth": { + "type": "AWS_IAM" + } + }, + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "uri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":apigateway:", + { + "Ref": "AWS::Region" + }, + ":lambda:path/2015-03-31/functions/", + { + "Fn::GetAtt": [ + "OpenApiGatewayToLambdaMessagesHandlerApiFunction0E78C5307", + "Arn" + ] + }, + "/invocations" + ] + ] + }, + "passthroughBehavior": "when_no_match", + "type": "aws_proxy" + }, + "x-amazon-apigateway-auth": { + "type": "AWS_IAM" + } + } + }, + "/photos": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "uri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":apigateway:", + { + "Ref": "AWS::Region" + }, + ":lambda:path/2015-03-31/functions/", + { + "Fn::GetAtt": [ + "OpenApiGatewayToLambdaPhotosHandlerApiFunction1C42C52E8", + "Arn" + ] + }, + "/invocations" + ] + ] + }, + "passthroughBehavior": "when_no_match", + "type": "aws_proxy" + }, + "x-amazon-apigateway-auth": { + "type": "AWS_IAM" + } + }, + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "uri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":apigateway:", + { + "Ref": "AWS::Region" + }, + ":lambda:path/2015-03-31/functions/", + { + "Fn::GetAtt": [ + "OpenApiGatewayToLambdaPhotosHandlerApiFunction1C42C52E8", + "Arn" + ] + }, + "/invocations" + ] + ] + }, + "passthroughBehavior": "when_no_match", + "type": "aws_proxy" + }, + "x-amazon-apigateway-auth": { + "type": "AWS_IAM" + } + } + } + } + }, + "Name": { + "Fn::Join": [ + "", + [ + "OpenApiGatewayToLambda-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + ] + } + } + }, + "OpenApiGatewayToLambdaSpecRestApiDeploymentC8BD07754bd94750334d61fd5d4bd8fcdf375863": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "Description": "Automatically created by the RestApi construct", + "RestApiId": { + "Ref": "OpenApiGatewayToLambdaSpecRestApi27C18064" + } + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W45", + "reason": "ApiGateway has AccessLogging enabled in AWS::ApiGateway::Stage resource, but cfn_nag checks for it in AWS::ApiGateway::Deployment resource" + } + ] + } + } + }, + "OpenApiGatewayToLambdaSpecRestApiDeploymentStageprodB962CBBB": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "AccessLogSetting": { + "DestinationArn": { + "Fn::GetAtt": [ + "OpenApiGatewayToLambdaApiAccessLogGroupE21284EC", + "Arn" + ] + }, + "Format": "{\"requestId\":\"$context.requestId\",\"ip\":\"$context.identity.sourceIp\",\"user\":\"$context.identity.user\",\"caller\":\"$context.identity.caller\",\"requestTime\":\"$context.requestTime\",\"httpMethod\":\"$context.httpMethod\",\"resourcePath\":\"$context.resourcePath\",\"status\":\"$context.status\",\"protocol\":\"$context.protocol\",\"responseLength\":\"$context.responseLength\"}" + }, + "DeploymentId": { + "Ref": "OpenApiGatewayToLambdaSpecRestApiDeploymentC8BD07754bd94750334d61fd5d4bd8fcdf375863" + }, + "MethodSettings": [ + { + "DataTraceEnabled": false, + "HttpMethod": "*", + "LoggingLevel": "INFO", + "ResourcePath": "/*" + } + ], + "RestApiId": { + "Ref": "OpenApiGatewayToLambdaSpecRestApi27C18064" + }, + "StageName": "prod", + "TracingEnabled": true + }, + "Metadata": { + "guard": { + "SuppressedRules": [ + "API_GW_CACHE_ENABLED_AND_ENCRYPTED" + ] + } + } + }, + "OpenApiGatewayToLambdaSpecRestApiUsagePlan44F6F5C6": { + "Type": "AWS::ApiGateway::UsagePlan", + "Properties": { + "ApiStages": [ + { + "ApiId": { + "Ref": "OpenApiGatewayToLambdaSpecRestApi27C18064" + }, + "Stage": { + "Ref": "OpenApiGatewayToLambdaSpecRestApiDeploymentStageprodB962CBBB" + }, + "Throttle": {} + } + ] + } + }, + "OpenApiGatewayToLambdaLambdaRestApiCloudWatchRoleBC7E6D95": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "apigateway.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:DescribeLogGroups", + "logs:DescribeLogStreams", + "logs:FilterLogEvents", + "logs:GetLogEvents", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "LambdaRestApiCloudWatchRolePolicy" + } + ] + }, + "Metadata": { + "guard": { + "SuppressedRules": [ + "IAM_NO_INLINE_POLICY_CHECK" + ] + } + } + }, + "OpenApiGatewayToLambdaLambdaRestApiAccount71EC7DA6": { + "Type": "AWS::ApiGateway::Account", + "Properties": { + "CloudWatchRoleArn": { + "Fn::GetAtt": [ + "OpenApiGatewayToLambdaLambdaRestApiCloudWatchRoleBC7E6D95", + "Arn" + ] + } + }, + "DependsOn": [ + "OpenApiGatewayToLambdaSpecRestApi27C18064" + ] + } + }, + "Outputs": { + "OpenApiGatewayToLambdaSpecRestApiEndpointD1FA5E3A": { + "Value": { + "Fn::Join": [ + "", + [ + "https://", + { + "Ref": "OpenApiGatewayToLambdaSpecRestApi27C18064" + }, + ".execute-api.", + { + "Ref": "AWS::Region" + }, + ".", + { + "Ref": "AWS::URLSuffix" + }, + "/", + { + "Ref": "OpenApiGatewayToLambdaSpecRestApiDeploymentStageprodB962CBBB" + }, + "/" + ] + ] + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/opilamapiFromInlineDefinitionIntegDefaultTestDeployAssertCF0E111E.assets.json b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/opilamapiFromInlineDefinitionIntegDefaultTestDeployAssertCF0E111E.assets.json new file mode 100644 index 000000000..5bb33cbe0 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/opilamapiFromInlineDefinitionIntegDefaultTestDeployAssertCF0E111E.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.20", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "opilamapiFromInlineDefinitionIntegDefaultTestDeployAssertCF0E111E.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/opilamapiFromInlineDefinitionIntegDefaultTestDeployAssertCF0E111E.template.json b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/opilamapiFromInlineDefinitionIntegDefaultTestDeployAssertCF0E111E.template.json new file mode 100644 index 000000000..ad9d0fb73 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/opilamapiFromInlineDefinitionIntegDefaultTestDeployAssertCF0E111E.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/tree.json b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/tree.json new file mode 100644 index 000000000..b8405f835 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.js.snapshot/tree.json @@ -0,0 +1,1029 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "opilam-apiFromInlineDefinition": { + "id": "opilam-apiFromInlineDefinition", + "path": "opilam-apiFromInlineDefinition", + "children": { + "OpenApiGatewayToLambda": { + "id": "OpenApiGatewayToLambda", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda", + "children": { + "MessagesHandlerApiFunction0ServiceRole": { + "id": "MessagesHandlerApiFunction0ServiceRole", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/MessagesHandlerApiFunction0ServiceRole", + "children": { + "ImportMessagesHandlerApiFunction0ServiceRole": { + "id": "ImportMessagesHandlerApiFunction0ServiceRole", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/MessagesHandlerApiFunction0ServiceRole/ImportMessagesHandlerApiFunction0ServiceRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "2.154.1" + } + }, + "Resource": { + "id": "Resource", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/MessagesHandlerApiFunction0ServiceRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "policies": [ + { + "policyName": "LambdaFunctionServiceRolePolicy", + "policyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/lambda/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "2.154.1" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/MessagesHandlerApiFunction0ServiceRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/MessagesHandlerApiFunction0ServiceRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": [ + "xray:PutTelemetryRecords", + "xray:PutTraceSegments" + ], + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "policyName": "OpenApiGatewayToLambdaMessagesHandlerApiFunction0ServiceRoleDefaultPolicy4563083B", + "roles": [ + { + "Ref": "OpenApiGatewayToLambdaMessagesHandlerApiFunction0ServiceRoleB5DC63A9" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnPolicy", + "version": "2.154.1" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Policy", + "version": "2.154.1" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "2.154.1" + } + }, + "MessagesHandlerApiFunction0": { + "id": "MessagesHandlerApiFunction0", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/MessagesHandlerApiFunction0", + "children": { + "Code": { + "id": "Code", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/MessagesHandlerApiFunction0/Code", + "children": { + "Stage": { + "id": "Stage", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/MessagesHandlerApiFunction0/Code/Stage", + "constructInfo": { + "fqn": "aws-cdk-lib.AssetStaging", + "version": "2.154.1" + } + }, + "AssetBucket": { + "id": "AssetBucket", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/MessagesHandlerApiFunction0/Code/AssetBucket", + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3.BucketBase", + "version": "2.154.1" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3_assets.Asset", + "version": "2.154.1" + } + }, + "Resource": { + "id": "Resource", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/MessagesHandlerApiFunction0/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::Function", + "aws:cdk:cloudformation:props": { + "code": { + "s3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "s3Key": "8ce85d10dcd7b8e6d43ffd909827afc76802ad40fcd7908886ff825cbe8e15df.zip" + }, + "environment": { + "variables": { + "AWS_NODEJS_CONNECTION_REUSE_ENABLED": "1" + } + }, + "handler": "index.handler", + "role": { + "Fn::GetAtt": [ + "OpenApiGatewayToLambdaMessagesHandlerApiFunction0ServiceRoleB5DC63A9", + "Arn" + ] + }, + "runtime": "nodejs20.x", + "tracingConfig": { + "mode": "Active" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.CfnFunction", + "version": "2.154.1" + } + }, + "PermitAPIGInvocation": { + "id": "PermitAPIGInvocation", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/MessagesHandlerApiFunction0/PermitAPIGInvocation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::Permission", + "aws:cdk:cloudformation:props": { + "action": "lambda:InvokeFunction", + "functionName": { + "Fn::GetAtt": [ + "OpenApiGatewayToLambdaMessagesHandlerApiFunction0E78C5307", + "Arn" + ] + }, + "principal": "apigateway.amazonaws.com", + "sourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":execute-api:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + { + "Ref": "OpenApiGatewayToLambdaSpecRestApi27C18064" + }, + "/*/*/*" + ] + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.CfnPermission", + "version": "2.154.1" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.Function", + "version": "2.154.1" + } + }, + "PhotosHandlerApiFunction1ServiceRole": { + "id": "PhotosHandlerApiFunction1ServiceRole", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/PhotosHandlerApiFunction1ServiceRole", + "children": { + "ImportPhotosHandlerApiFunction1ServiceRole": { + "id": "ImportPhotosHandlerApiFunction1ServiceRole", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/PhotosHandlerApiFunction1ServiceRole/ImportPhotosHandlerApiFunction1ServiceRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "2.154.1" + } + }, + "Resource": { + "id": "Resource", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/PhotosHandlerApiFunction1ServiceRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "policies": [ + { + "policyName": "LambdaFunctionServiceRolePolicy", + "policyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/lambda/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "2.154.1" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/PhotosHandlerApiFunction1ServiceRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/PhotosHandlerApiFunction1ServiceRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": [ + "xray:PutTelemetryRecords", + "xray:PutTraceSegments" + ], + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "policyName": "OpenApiGatewayToLambdaPhotosHandlerApiFunction1ServiceRoleDefaultPolicy389AA3BA", + "roles": [ + { + "Ref": "OpenApiGatewayToLambdaPhotosHandlerApiFunction1ServiceRoleA572C749" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnPolicy", + "version": "2.154.1" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Policy", + "version": "2.154.1" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "2.154.1" + } + }, + "PhotosHandlerApiFunction1": { + "id": "PhotosHandlerApiFunction1", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/PhotosHandlerApiFunction1", + "children": { + "Code": { + "id": "Code", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/PhotosHandlerApiFunction1/Code", + "children": { + "Stage": { + "id": "Stage", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/PhotosHandlerApiFunction1/Code/Stage", + "constructInfo": { + "fqn": "aws-cdk-lib.AssetStaging", + "version": "2.154.1" + } + }, + "AssetBucket": { + "id": "AssetBucket", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/PhotosHandlerApiFunction1/Code/AssetBucket", + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3.BucketBase", + "version": "2.154.1" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3_assets.Asset", + "version": "2.154.1" + } + }, + "Resource": { + "id": "Resource", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/PhotosHandlerApiFunction1/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::Function", + "aws:cdk:cloudformation:props": { + "code": { + "s3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "s3Key": "654d49d4ea47a6be417d57b94dc0310933d0e971a3e48a3080c3e48487af3e50.zip" + }, + "environment": { + "variables": { + "AWS_NODEJS_CONNECTION_REUSE_ENABLED": "1" + } + }, + "handler": "index.handler", + "role": { + "Fn::GetAtt": [ + "OpenApiGatewayToLambdaPhotosHandlerApiFunction1ServiceRoleA572C749", + "Arn" + ] + }, + "runtime": "nodejs20.x", + "tracingConfig": { + "mode": "Active" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.CfnFunction", + "version": "2.154.1" + } + }, + "PermitAPIGInvocation": { + "id": "PermitAPIGInvocation", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/PhotosHandlerApiFunction1/PermitAPIGInvocation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::Permission", + "aws:cdk:cloudformation:props": { + "action": "lambda:InvokeFunction", + "functionName": { + "Fn::GetAtt": [ + "OpenApiGatewayToLambdaPhotosHandlerApiFunction1C42C52E8", + "Arn" + ] + }, + "principal": "apigateway.amazonaws.com", + "sourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":execute-api:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + { + "Ref": "OpenApiGatewayToLambdaSpecRestApi27C18064" + }, + "/*/*/*" + ] + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.CfnPermission", + "version": "2.154.1" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.Function", + "version": "2.154.1" + } + }, + "ApiAccessLogGroup": { + "id": "ApiAccessLogGroup", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/ApiAccessLogGroup", + "children": { + "Resource": { + "id": "Resource", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/ApiAccessLogGroup/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Logs::LogGroup", + "aws:cdk:cloudformation:props": {} + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_logs.CfnLogGroup", + "version": "2.154.1" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_logs.LogGroup", + "version": "2.154.1" + } + }, + "SpecRestApi": { + "id": "SpecRestApi", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/SpecRestApi", + "children": { + "Resource": { + "id": "Resource", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/SpecRestApi/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ApiGateway::RestApi", + "aws:cdk:cloudformation:props": { + "body": { + "openapi": "3.0.1", + "info": { + "title": "api", + "version": "2023-02-20T20:46:08Z" + }, + "paths": { + "/messages": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "uri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":apigateway:", + { + "Ref": "AWS::Region" + }, + ":lambda:path/2015-03-31/functions/", + { + "Fn::GetAtt": [ + "OpenApiGatewayToLambdaMessagesHandlerApiFunction0E78C5307", + "Arn" + ] + }, + "/invocations" + ] + ] + }, + "passthroughBehavior": "when_no_match", + "type": "aws_proxy" + }, + "x-amazon-apigateway-auth": { + "type": "AWS_IAM" + } + }, + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "uri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":apigateway:", + { + "Ref": "AWS::Region" + }, + ":lambda:path/2015-03-31/functions/", + { + "Fn::GetAtt": [ + "OpenApiGatewayToLambdaMessagesHandlerApiFunction0E78C5307", + "Arn" + ] + }, + "/invocations" + ] + ] + }, + "passthroughBehavior": "when_no_match", + "type": "aws_proxy" + }, + "x-amazon-apigateway-auth": { + "type": "AWS_IAM" + } + } + }, + "/photos": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "uri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":apigateway:", + { + "Ref": "AWS::Region" + }, + ":lambda:path/2015-03-31/functions/", + { + "Fn::GetAtt": [ + "OpenApiGatewayToLambdaPhotosHandlerApiFunction1C42C52E8", + "Arn" + ] + }, + "/invocations" + ] + ] + }, + "passthroughBehavior": "when_no_match", + "type": "aws_proxy" + }, + "x-amazon-apigateway-auth": { + "type": "AWS_IAM" + } + }, + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "uri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":apigateway:", + { + "Ref": "AWS::Region" + }, + ":lambda:path/2015-03-31/functions/", + { + "Fn::GetAtt": [ + "OpenApiGatewayToLambdaPhotosHandlerApiFunction1C42C52E8", + "Arn" + ] + }, + "/invocations" + ] + ] + }, + "passthroughBehavior": "when_no_match", + "type": "aws_proxy" + }, + "x-amazon-apigateway-auth": { + "type": "AWS_IAM" + } + } + } + } + }, + "name": { + "Fn::Join": [ + "", + [ + "OpenApiGatewayToLambda-", + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "/", + { + "Ref": "AWS::StackId" + } + ] + } + ] + } + ] + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_apigateway.CfnRestApi", + "version": "2.154.1" + } + }, + "Default": { + "id": "Default", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/SpecRestApi/Default", + "constructInfo": { + "fqn": "aws-cdk-lib.aws_apigateway.ResourceBase", + "version": "2.154.1" + } + }, + "Deployment": { + "id": "Deployment", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/SpecRestApi/Deployment", + "children": { + "Resource": { + "id": "Resource", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/SpecRestApi/Deployment/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ApiGateway::Deployment", + "aws:cdk:cloudformation:props": { + "description": "Automatically created by the RestApi construct", + "restApiId": { + "Ref": "OpenApiGatewayToLambdaSpecRestApi27C18064" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_apigateway.CfnDeployment", + "version": "2.154.1" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_apigateway.Deployment", + "version": "2.154.1" + } + }, + "DeploymentStage.prod": { + "id": "DeploymentStage.prod", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/SpecRestApi/DeploymentStage.prod", + "children": { + "Resource": { + "id": "Resource", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/SpecRestApi/DeploymentStage.prod/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ApiGateway::Stage", + "aws:cdk:cloudformation:props": { + "accessLogSetting": { + "destinationArn": { + "Fn::GetAtt": [ + "OpenApiGatewayToLambdaApiAccessLogGroupE21284EC", + "Arn" + ] + }, + "format": "{\"requestId\":\"$context.requestId\",\"ip\":\"$context.identity.sourceIp\",\"user\":\"$context.identity.user\",\"caller\":\"$context.identity.caller\",\"requestTime\":\"$context.requestTime\",\"httpMethod\":\"$context.httpMethod\",\"resourcePath\":\"$context.resourcePath\",\"status\":\"$context.status\",\"protocol\":\"$context.protocol\",\"responseLength\":\"$context.responseLength\"}" + }, + "deploymentId": { + "Ref": "OpenApiGatewayToLambdaSpecRestApiDeploymentC8BD07754bd94750334d61fd5d4bd8fcdf375863" + }, + "methodSettings": [ + { + "httpMethod": "*", + "resourcePath": "/*", + "dataTraceEnabled": false, + "loggingLevel": "INFO" + } + ], + "restApiId": { + "Ref": "OpenApiGatewayToLambdaSpecRestApi27C18064" + }, + "stageName": "prod", + "tracingEnabled": true + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_apigateway.CfnStage", + "version": "2.154.1" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_apigateway.Stage", + "version": "2.154.1" + } + }, + "Endpoint": { + "id": "Endpoint", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/SpecRestApi/Endpoint", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnOutput", + "version": "2.154.1" + } + }, + "UsagePlan": { + "id": "UsagePlan", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/SpecRestApi/UsagePlan", + "children": { + "Resource": { + "id": "Resource", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/SpecRestApi/UsagePlan/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ApiGateway::UsagePlan", + "aws:cdk:cloudformation:props": { + "apiStages": [ + { + "apiId": { + "Ref": "OpenApiGatewayToLambdaSpecRestApi27C18064" + }, + "stage": { + "Ref": "OpenApiGatewayToLambdaSpecRestApiDeploymentStageprodB962CBBB" + }, + "throttle": {} + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_apigateway.CfnUsagePlan", + "version": "2.154.1" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_apigateway.UsagePlan", + "version": "2.154.1" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_apigateway.SpecRestApi", + "version": "2.154.1" + } + }, + "LambdaRestApiCloudWatchRole": { + "id": "LambdaRestApiCloudWatchRole", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/LambdaRestApiCloudWatchRole", + "children": { + "ImportLambdaRestApiCloudWatchRole": { + "id": "ImportLambdaRestApiCloudWatchRole", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/LambdaRestApiCloudWatchRole/ImportLambdaRestApiCloudWatchRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "2.154.1" + } + }, + "Resource": { + "id": "Resource", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/LambdaRestApiCloudWatchRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "apigateway.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "policies": [ + { + "policyName": "LambdaRestApiCloudWatchRolePolicy", + "policyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:DescribeLogGroups", + "logs:DescribeLogStreams", + "logs:FilterLogEvents", + "logs:GetLogEvents", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":*" + ] + ] + } + } + ], + "Version": "2012-10-17" + } + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "2.154.1" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "2.154.1" + } + }, + "LambdaRestApiAccount": { + "id": "LambdaRestApiAccount", + "path": "opilam-apiFromInlineDefinition/OpenApiGatewayToLambda/LambdaRestApiAccount", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ApiGateway::Account", + "aws:cdk:cloudformation:props": { + "cloudWatchRoleArn": { + "Fn::GetAtt": [ + "OpenApiGatewayToLambdaLambdaRestApiCloudWatchRoleBC7E6D95", + "Arn" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_apigateway.CfnAccount", + "version": "2.154.1" + } + } + }, + "constructInfo": { + "fqn": "@aws-solutions-constructs/aws-openapigateway-lambda.OpenApiGatewayToLambda", + "version": "2.67.1" + } + }, + "Integ": { + "id": "Integ", + "path": "opilam-apiFromInlineDefinition/Integ", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "opilam-apiFromInlineDefinition/Integ/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "opilam-apiFromInlineDefinition/Integ/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "opilam-apiFromInlineDefinition/Integ/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "opilam-apiFromInlineDefinition/Integ/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "2.154.1" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "opilam-apiFromInlineDefinition/Integ/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "2.154.1" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "2.154.1" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "2.154.1-alpha.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "2.154.1-alpha.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "opilam-apiFromInlineDefinition/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "2.154.1" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "opilam-apiFromInlineDefinition/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "2.154.1" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "2.154.1" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "2.154.1" + } + } +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.ts b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.ts new file mode 100644 index 000000000..b071e51ee --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/integ.opilam-apiFromInlineDefinition.ts @@ -0,0 +1,51 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +import { App, Stack } from "aws-cdk-lib"; +import { OpenApiGatewayToLambda } from "../lib"; +import * as lambda from 'aws-cdk-lib/aws-lambda'; +import * as defaults from '@aws-solutions-constructs/core'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; + +import * as jsonApiDefinition from './openapi/apiDefinition.json'; + +const app = new App(); +const stack = new Stack(app, defaults.generateIntegStackName(__filename)); +stack.templateOptions.description = 'Integration Test for aws-openapigateway-lambda'; + +new OpenApiGatewayToLambda(stack, 'OpenApiGatewayToLambda', { + apiDefinitionJson: jsonApiDefinition, + apiIntegrations: [ + { + id: 'MessagesHandler', + lambdaFunctionProps: { + runtime: defaults.COMMERCIAL_REGION_LAMBDA_NODE_RUNTIME, + handler: 'index.handler', + code: lambda.Code.fromAsset(`${__dirname}/messages-lambda`), + } + }, + { + id: 'PhotosHandler', + lambdaFunctionProps: { + runtime: defaults.COMMERCIAL_REGION_LAMBDA_NODE_RUNTIME, + handler: 'index.handler', + code: lambda.Code.fromAsset(`${__dirname}/photos-lambda`), + } + } + ] +}); + +// Synth +new IntegTest(stack, 'Integ', { testCases: [ + stack +] }); diff --git a/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/openapi/apiDefinition.json b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/openapi/apiDefinition.json new file mode 100644 index 000000000..90dfcac71 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/openapi/apiDefinition.json @@ -0,0 +1,57 @@ +{ + "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" + }, + "x-amazon-apigateway-auth": { + "type": "AWS_IAM" + } + }, + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "uri": "MessagesHandler", + "passthroughBehavior": "when_no_match", + "type": "aws_proxy" + }, + "x-amazon-apigateway-auth": { + "type": "AWS_IAM" + } + } + }, + "/photos": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "uri": "PhotosHandler", + "passthroughBehavior": "when_no_match", + "type": "aws_proxy" + }, + "x-amazon-apigateway-auth": { + "type": "AWS_IAM" + } + }, + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "uri": "PhotosHandler", + "passthroughBehavior": "when_no_match", + "type": "aws_proxy" + }, + "x-amazon-apigateway-auth": { + "type": "AWS_IAM" + } + } + } + } + } \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/test.openapigateway-lambda.test.ts b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/test.openapigateway-lambda.test.ts index 4c146de15..a95392b90 100644 --- a/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/test.openapigateway-lambda.test.ts +++ b/source/patterns/@aws-solutions-constructs/aws-openapigateway-lambda/test/test.openapigateway-lambda.test.ts @@ -12,8 +12,9 @@ */ // Imports -import { Stack } from "aws-cdk-lib"; +import { Stack, Duration } from "aws-cdk-lib"; import { OpenApiGatewayToLambda } from "../lib"; +import { ObtainApiDefinition, CheckOpenApiProps } from "../lib/openapi-helper"; import * as lambda from 'aws-cdk-lib/aws-lambda'; import * as s3 from 'aws-cdk-lib/aws-s3'; import { Asset } from "aws-cdk-lib/aws-s3-assets"; @@ -21,6 +22,7 @@ import * as path from 'path'; import { Template } from "aws-cdk-lib/assertions"; import { CreateScrapBucket } from "@aws-solutions-constructs/core"; import * as defaults from '@aws-solutions-constructs/core'; +import * as inlineJsonApiDefinition from './openapi/apiDefinition.json'; test('Simple deployment works', () => { const stack = new Stack(); @@ -101,6 +103,30 @@ test('API Definition can be specified from S3 Bucket and Key', () => { expect(construct.apiLambdaFunctions[0].lambdaFunction).toBeDefined(); }); +test('API Definition can be specified from an apiDefinitionJson ', () => { + const stack = new Stack(); + + const construct = new OpenApiGatewayToLambda(stack, 'test-apigateway-lambda', { + apiDefinitionJson: inlineJsonApiDefinition, + apiIntegrations: [ + { + id: 'MessagesHandler', + lambdaFunctionProps: { + runtime: defaults.COMMERCIAL_REGION_LAMBDA_NODE_RUNTIME, + handler: 'index.handler', + code: lambda.Code.fromAsset(`${__dirname}/messages-lambda`), + } + } + ] + }); + + expect(construct.apiLambdaFunctions[0].id).toEqual('MessagesHandler'); + expect(construct.apiLambdaFunctions[0].lambdaFunction).toBeDefined(); +}); + +const incorrectDefinitionMessage = + 'Exactly one of apiDefinitionAsset, apiDefinitionJson or (apiDefinitionBucket/apiDefinitionKey) must be provided'; + test('Throws error when both api definition asset and s3 object are specified', () => { const stack = new Stack(); @@ -125,7 +151,7 @@ test('Throws error when both api definition asset and s3 object are specified', ] }); }; - expect(app).toThrowError('Either apiDefinitionBucket/apiDefinitionKey or apiDefinitionAsset must be specified, but not both'); + expect(app).toThrowError(incorrectDefinitionMessage); }); test('Multiple Lambda Functions can be specified', () => { @@ -224,103 +250,6 @@ test('Throws error when neither existingLambdaObj or lambdaFunctionProps is spec expect(app).toThrowError('One of existingLambdaObj or lambdaFunctionProps must be specified for the api integration with id: MessagesHandler'); }); -test('Throws error when no api definition is specified', () => { - const stack = new Stack(); - - const app = () => { - new OpenApiGatewayToLambda(stack, 'test-apigateway-lambda', { - apiIntegrations: [ - { - id: 'MessagesHandler', - lambdaFunctionProps: { - runtime: defaults.COMMERCIAL_REGION_LAMBDA_NODE_RUNTIME, - handler: 'index.handler', - code: lambda.Code.fromAsset(`${__dirname}/messages-lambda`), - } - } - ] - }); - }; - expect(app).toThrowError('Either apiDefinitionBucket/apiDefinitionKey or apiDefinitionAsset must be specified'); -}); - -test('Throws error when no api integration is specified', () => { - const stack = new Stack(); - - const apiDefinitionAsset = new Asset(stack, 'OpenApiAsset', { - path: path.join(__dirname, 'openapi/apiDefinition.yaml') - }); - - const app = () => { - new OpenApiGatewayToLambda(stack, 'test-apigateway-lambda', { - apiDefinitionAsset, - apiIntegrations: [] - }); - }; - expect(app).toThrowError('At least one ApiIntegration must be specified in the apiIntegrations property'); -}); - -test('Throws error when api definition s3 bucket is specified but s3 object key is missing', () => { - const stack = new Stack(); - - const apiDefinitionBucket = CreateScrapBucket(stack, "scrapBucket"); - - const app = () => { - new OpenApiGatewayToLambda(stack, 'test-apigateway-lambda', { - apiDefinitionBucket, - apiIntegrations: [] - }); - }; - expect(app).toThrowError('Either apiDefinitionBucket/apiDefinitionKey or apiDefinitionAsset must be specified'); -}); - -test('Throws error when api definition s3 object key is specified but s3 bucket is missing', () => { - const stack = new Stack(); - - const app = () => { - new OpenApiGatewayToLambda(stack, 'test-apigateway-lambda', { - apiDefinitionKey: 'prefix/api-definition.yml', - apiIntegrations: [] - }); - }; - expect(app).toThrowError('Either apiDefinitionBucket/apiDefinitionKey or apiDefinitionAsset must be specified'); -}); - -test('Throws error when api is defined as asset and s3 bucket is specified', () => { - const stack = new Stack(); - - const apiDefinitionAsset = new Asset(stack, 'OpenApiAsset', { - path: path.join(__dirname, 'openapi/apiDefinition.yaml') - }); - const apiDefinitionBucket = CreateScrapBucket(stack, "scrapBucket"); - - const app = () => { - new OpenApiGatewayToLambda(stack, 'test-apigateway-lambda', { - apiDefinitionAsset, - apiDefinitionBucket, - apiIntegrations: [] - }); - }; - expect(app).toThrowError('Either apiDefinitionBucket/apiDefinitionKey or apiDefinitionAsset must be specified'); -}); - -test('Throws error when api is defined as asset and s3 key is specified', () => { - const stack = new Stack(); - - const apiDefinitionAsset = new Asset(stack, 'OpenApiAsset', { - path: path.join(__dirname, 'openapi/apiDefinition.yaml') - }); - - const app = () => { - new OpenApiGatewayToLambda(stack, 'test-apigateway-lambda', { - apiDefinitionAsset, - apiDefinitionKey: 'prefix/api-definition.yml', - apiIntegrations: [] - }); - }; - expect(app).toThrowError('Either apiDefinitionBucket/apiDefinitionKey or apiDefinitionAsset must be specified'); -}); - test('Two Constructs create APIs with different names', () => { const stack = new Stack(); @@ -440,7 +369,7 @@ test('Confirm API definition uri is added to function name', () => { }); -test('Confirm that providing both lambdaFunction and functionProps is an error', () => { +test('Confirm that providing both lambdaFunction and functionProps is an error', () => { const stack = new Stack(); const existingLambdaObj = new lambda.Function(stack, 'ExistingLambda', { runtime: defaults.COMMERCIAL_REGION_LAMBDA_NODE_RUNTIME, @@ -469,7 +398,313 @@ test('Confirm that providing both lambdaFunction and functionProps is an error' }; const app = () => { - new OpenApiGatewayToLambda(stack, 'test-apigateway-lambda', props); + new OpenApiGatewayToLambda(stack, 'test', props); }; expect(app).toThrowError(`Error - Cannot provide both lambdaFunctionProps and existingLambdaObj in an ApiIntegrationfor the api integration with id: MessagesHandler`); }); + +/* + * openapi-helper.ts tests +*/ +test('Throws error when no api definition is specified', () => { + const app = () => { + CheckOpenApiProps({ + apiIntegrations: [ + { + id: 'MessagesHandler', + lambdaFunctionProps: { + runtime: defaults.COMMERCIAL_REGION_LAMBDA_NODE_RUNTIME, + handler: 'index.handler', + code: lambda.Code.fromAsset(`${__dirname}/messages-lambda`), + } + } + ] + }); + }; + expect(app).toThrowError(incorrectDefinitionMessage); +}); + +test('Throws error when no api integration is specified', () => { + const stack = new Stack(); + + const apiDefinitionAsset = new Asset(stack, 'OpenApiAsset', { + path: path.join(__dirname, 'openapi/apiDefinition.yaml') + }); + + const app = () => { + CheckOpenApiProps({ + apiDefinitionAsset, + apiIntegrations: [] + }); + }; + expect(app).toThrowError('At least one ApiIntegration must be specified in the apiIntegrations property'); +}); + +test('Throws error when api definition s3 bucket is specified but s3 object key is missing', () => { + const stack = new Stack(); + + const apiDefinitionBucket = CreateScrapBucket(stack, "scrapBucket"); + + const app = () => { + CheckOpenApiProps({ + apiDefinitionBucket, + apiIntegrations: [] + }); + }; + expect(app).toThrowError('apiDefinitionBucket and apiDefinitionKey must be specified together.'); +}); + +test('Throws error when api definition s3 object key is specified but s3 bucket is missing', () => { + const app = () => { + CheckOpenApiProps({ + apiDefinitionKey: 'prefix/api-definition.yml', + apiIntegrations: [] + }); + }; + expect(app).toThrowError('apiDefinitionBucket and apiDefinitionKey must be specified together.'); +}); + +test('Throws error when api is defined as asset and s3 bucket is specified', () => { + const stack = new Stack(); + + const apiDefinitionAsset = new Asset(stack, 'OpenApiAsset', { + path: path.join(__dirname, 'openapi/apiDefinition.yaml') + }); + const apiDefinitionBucket = CreateScrapBucket(stack, "scrapBucket"); + + const app = () => { + CheckOpenApiProps({ + apiDefinitionAsset, + apiDefinitionBucket, + apiDefinitionKey: 'prefix/api-definition.yml', + apiIntegrations: [ + { + id: 'MessagesHandler', + lambdaFunctionProps: { + runtime: defaults.COMMERCIAL_REGION_LAMBDA_NODE_RUNTIME, + handler: 'index.handler', + code: lambda.Code.fromAsset(`${__dirname}/messages-lambda`), + } + } + ] + }); + }; + expect(app).toThrowError(incorrectDefinitionMessage); +}); + +test('Throws error when api is defined as asset and s3 key is specified', () => { + const stack = new Stack(); + + const apiDefinitionAsset = new Asset(stack, 'OpenApiAsset', { + path: path.join(__dirname, 'openapi/apiDefinition.yaml') + }); + const apiDefinitionBucket = CreateScrapBucket(stack, "scrapBucket"); + + const app = () => { + CheckOpenApiProps({ + apiDefinitionAsset, + apiDefinitionBucket, + apiDefinitionKey: 'prefix/api-definition.yml', + apiIntegrations: [ + { + id: 'MessagesHandler', + lambdaFunctionProps: { + runtime: defaults.COMMERCIAL_REGION_LAMBDA_NODE_RUNTIME, + handler: 'index.handler', + code: lambda.Code.fromAsset(`${__dirname}/messages-lambda`), + } + } + ] + }); + }; + expect(app).toThrowError(incorrectDefinitionMessage); +}); + +test('Throws error when both api definition inline and api definition asset are specified', () => { + const stack = new Stack(); + + const apiDefinitionAsset = new Asset(stack, 'OpenApiAsset', { + path: path.join(__dirname, 'openapi/apiDefinition.yaml') + }); + + const app = () => { + CheckOpenApiProps({ + apiDefinitionAsset, + apiDefinitionJson: inlineJsonApiDefinition, + apiIntegrations: [ + { + id: 'MessagesHandler', + lambdaFunctionProps: { + runtime: defaults.COMMERCIAL_REGION_LAMBDA_NODE_RUNTIME, + handler: 'index.handler', + code: lambda.Code.fromAsset(`${__dirname}/messages-lambda`), + } + } + ] + }); + }; + expect(app).toThrowError(incorrectDefinitionMessage); +}); + +test('Throws error when both api definition inline and s3 object are specified', () => { + const stack = new Stack(); + + const app = () => { + CheckOpenApiProps({ + apiDefinitionJson: inlineJsonApiDefinition, + apiDefinitionBucket: new s3.Bucket(stack, 'ApiDefinitionBucket'), + apiDefinitionKey: 'key', + apiIntegrations: [ + { + id: 'MessagesHandler', + lambdaFunctionProps: { + runtime: defaults.COMMERCIAL_REGION_LAMBDA_NODE_RUNTIME, + handler: 'index.handler', + code: lambda.Code.fromAsset(`${__dirname}/messages-lambda`), + } + } + ] + }); + }; + expect(app).toThrowError(incorrectDefinitionMessage); +}); + +test('ObtainApiDefinition from local asset', () => { + const stack = new Stack(); + + const apiDefinitionAsset = new Asset(stack, 'OpenApiAsset', { + path: path.join(__dirname, 'openapi/apiDefinition.yaml') + }); + + const testFunction = new lambda.Function(stack, 'test', { + runtime: defaults.COMMERCIAL_REGION_LAMBDA_NODE_RUNTIME, + handler: 'index.handler', + code: lambda.Code.fromAsset(`${__dirname}/messages-lambda`), + }); + + const apiLambdaFunctions = [ + { + id: 'MessageHandler', + lambdaFunction: testFunction + } + ]; + + const api = ObtainApiDefinition(stack, + { + apiDefinitionAsset, + tokenToFunctionMap: apiLambdaFunctions + }); + + expect(api).toBeDefined(); + expect((api as any).bucketName).toBeDefined(); + expect((api as any).key).toBeDefined(); + + const template = Template.fromStack(stack); + template.resourceCountIs("Custom::TemplateWriter", 1); +}); + +test('ObtainApiDefinition from inline JSON spec', () => { + const stack = new Stack(); + + const testFunction = new lambda.Function(stack, 'test', { + runtime: defaults.COMMERCIAL_REGION_LAMBDA_NODE_RUNTIME, + handler: 'index.handler', + code: lambda.Code.fromAsset(`${__dirname}/messages-lambda`), + }); + + const apiLambdaFunctions = [ + { + id: 'MessageHandler', + lambdaFunction: testFunction + } + ]; + + const api = ObtainApiDefinition(stack, + { + apiJsonDefinition: inlineJsonApiDefinition, + tokenToFunctionMap: apiLambdaFunctions + }); + + expect(api).toBeDefined(); + expect((api as any).definition).toBeDefined(); + expect((api as any).definition.openapi).toEqual("3.0.1"); + expect((api as any).definition.info).toBeDefined(); + expect((api as any).definition.paths).toBeDefined(); + + const template = Template.fromStack(stack); + template.resourceCountIs("Custom::TemplateWriter", 0); + +}); + +test('ObtainApiDefinition from S3 bucket/key', () => { + const stack = new Stack(); + + const testFunction = new lambda.Function(stack, 'test', { + runtime: defaults.COMMERCIAL_REGION_LAMBDA_NODE_RUNTIME, + handler: 'index.handler', + code: lambda.Code.fromAsset(`${__dirname}/messages-lambda`), + }); + + const apiLambdaFunctions = [ + { + id: 'MessageHandler', + lambdaFunction: testFunction + } + ]; + + const api = ObtainApiDefinition(stack, + { + apiDefinitionBucket: CreateScrapBucket(stack, "scrapBucket"), + apiDefinitionKey: "api.yml", + tokenToFunctionMap: apiLambdaFunctions + }); + expect(api).toBeDefined(); + expect((api as any).bucketName).toBeDefined(); + expect((api as any).key).toBeDefined(); + + const template = Template.fromStack(stack); + template.resourceCountIs("Custom::TemplateWriter", 1); + +}); + +test('ObtainApiDefinition uses custom properties', () => { + const stack = new Stack(); + + const apiDefinitionAsset = new Asset(stack, 'OpenApiAsset', { + path: path.join(__dirname, 'openapi/apiDefinition.yaml') + }); + + const testFunction = new lambda.Function(stack, 'test', { + runtime: defaults.COMMERCIAL_REGION_LAMBDA_NODE_RUNTIME, + handler: 'index.handler', + code: lambda.Code.fromAsset(`${__dirname}/messages-lambda`), + }); + + const apiLambdaFunctions = [ + { + id: 'MessageHandler', + lambdaFunction: testFunction + } + ]; + + const api = ObtainApiDefinition(stack, + { + apiDefinitionAsset, + tokenToFunctionMap: apiLambdaFunctions, + internalTransformTimeout: Duration.seconds(385), + internalTransformMemorySize: 3456 + }); + + expect(api).toBeDefined(); + expect((api as any).bucketName).toBeDefined(); + expect((api as any).key).toBeDefined(); + + const template = Template.fromStack(stack); + template.resourceCountIs("Custom::TemplateWriter", 1); + template.hasResource("AWS::Lambda::Function", { + Properties: { + Timeout: 385, + MemorySize: 3456 + } + }); +}); From a2278ab6ab03dd7b3b3d4ba1dac207ee57c30043 Mon Sep 17 00:00:00 2001 From: AWS Solutions Constructs Automation Date: Wed, 11 Sep 2024 02:12:59 +0000 Subject: [PATCH 5/6] chore(release): 2.69.0 --- CHANGELOG.md | 7 +++++++ source/lerna.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95342227a..0358bf1b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [2.69.0](https://github.com/awslabs/aws-solutions-constructs/compare/v2.68.0...v2.69.0) (2024-09-11) + + +### Features + +* **aws-openapigateway-lambda:** update construct to allow specifying an inline api definition ([#1190](https://github.com/awslabs/aws-solutions-constructs/issues/1190)) ([b1baf59](https://github.com/awslabs/aws-solutions-constructs/commit/b1baf59c13d6270cd1e32dcaed7dae3c6b302087)) + ## [2.68.0](https://github.com/awslabs/aws-solutions-constructs/compare/v2.67.1...v2.68.0) (2024-08-28) Built on CDK v2.154.1 diff --git a/source/lerna.json b/source/lerna.json index b9f133276..df38c2782 100644 --- a/source/lerna.json +++ b/source/lerna.json @@ -6,5 +6,5 @@ "./patterns/@aws-solutions-constructs/*" ], "rejectCycles": "true", - "version": "2.68.0" + "version": "2.69.0" } From c09ebf47595a5fb3671b3492efb7a9db634693f2 Mon Sep 17 00:00:00 2001 From: biffgaut Date: Tue, 10 Sep 2024 22:16:18 -0400 Subject: [PATCH 6/6] chore(changelog): Updated CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0358bf1b5..1625df07c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file. See [standa ## [2.69.0](https://github.com/awslabs/aws-solutions-constructs/compare/v2.68.0...v2.69.0) (2024-09-11) +Built on CDK v2.154.1 ### Features