Skip to content

Commit

Permalink
Implemented feature
Browse files Browse the repository at this point in the history
  • Loading branch information
biffgaut committed Feb 4, 2025
1 parent 97c69fe commit e9c4704
Show file tree
Hide file tree
Showing 24 changed files with 3,549 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -126,14 +126,15 @@ export class OpenApiGatewayToLambda extends Construct {
lambdaFunction: defaults.buildLambdaFunction(this, {
existingLambdaObj: apiIntegration.existingLambdaObj,
lambdaFunctionProps: apiIntegration.lambdaFunctionProps
}, `${apiIntegration.id}ApiFunction${lambdaCounter++}`)
}, `${apiIntegration.id}ApiFunction${lambdaCounter++}`),
functionAlias: apiIntegration.existingFunctionAlias
};
} else {
throw new Error(`One of existingLambdaObj or lambdaFunctionProps must be specified for the api integration with id: ${apiIntegration.id}`);
}
});

const definition = ObtainApiDefinition(this, {
const definition = ObtainApiDefinition(this, {
tokenToFunctionMap: this.apiLambdaFunctions,
apiDefinitionBucket: props.apiDefinitionBucket,
apiDefinitionKey: props.apiDefinitionKey,
Expand All @@ -157,11 +158,19 @@ export class OpenApiGatewayToLambda extends Construct {
this.apiLambdaFunctions.forEach(apiLambdaFunction => {
// Redeploy the API any time one of the lambda functions changes
this.apiGateway.latestDeployment?.addToLogicalId(apiLambdaFunction.lambdaFunction.functionArn);
// Grant APIGW invocation rights for each lambda function
apiLambdaFunction.lambdaFunction.addPermission(`${id}PermitAPIGInvocation`, {
principal: new iam.ServicePrincipal('apigateway.amazonaws.com'),
sourceArn: this.apiGateway.arnForExecuteApi('*')
});
if (apiLambdaFunction.functionAlias) {
// Grant APIGW invocation rights for each lambda function
apiLambdaFunction.functionAlias.addPermission(`${id}PermitAPIGInvocation`, {
principal: new iam.ServicePrincipal('apigateway.amazonaws.com'),
sourceArn: this.apiGateway.arnForExecuteApi('*')
});
} else {
// Grant APIGW invocation rights for each lambda function
apiLambdaFunction.lambdaFunction.addPermission(`${id}PermitAPIGInvocation`, {
principal: new iam.ServicePrincipal('apigateway.amazonaws.com'),
sourceArn: this.apiGateway.arnForExecuteApi('*')
});
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ export interface ApiIntegration {
* One and only one of existingLambdaObj or lambdaFunctionProps must be specified, any other combination will cause an error.
*/
readonly existingLambdaObj?: lambda.Function;
/**
* An Alias to the provided existingLambdbaObj that should be used in the integration with the API
*
* This is only valid if and existingLambdaObj is specified
*/
readonly existingFunctionAlias?: lambda.Alias;
/**
* Properties for the Lambda function to create and associate with the API method in the OpenAPI file matched by id.
*
Expand All @@ -68,6 +74,10 @@ export interface ApiLambdaFunction {
* The instantiated lambda.Function.
*/
readonly lambdaFunction: lambda.Function;
/**
* Optional Alias to the function - if provided, it will be used instead of the function whereever possible.
*/
readonly functionAlias?: lambda.Alias;
}

export interface OpenApiProps {
Expand Down Expand Up @@ -136,7 +146,8 @@ export function ObtainApiDefinition(scope: Construct, props: ObtainApiDefinition
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`;
const targetArn = apiLambdaFunction.functionAlias ? apiLambdaFunction.functionAlias.functionArn : apiLambdaFunction.lambdaFunction.functionArn;
const uriResolvedValue = `arn:${Aws.PARTITION}:apigateway:${Aws.REGION}:lambda:path/2015-03-31/functions/${targetArn}/invocations`;

return {
id: uriPlaceholderString,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export const handler = async (event) => {
switch (event.httpMethod) {
case 'POST':
return {
statusCode: 200,
body: JSON.stringify({"message": "NEW - successfully handled POST from messages lambda"})
};
case 'GET':
return {
statusCode: 200,
body: JSON.stringify({"message": "NEW - successfully handled GET from messages lambda"})
};
default:
throw new Error(`cannot handle httpMethod: ${event.httpMethod}`);
}
};

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/**
* 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 { S3Client, GetObjectCommand, PutObjectCommand } from "@aws-sdk/client-s3";
import * as crypto from 'crypto';

const s3Client = new S3Client({ region: process.env.REGION });

export const handler = async (event: any, context: any) => {
let status = 'SUCCESS';
let responseData = {};

// These are the standard Create/Update/Delete custom resource request types defined here:
// https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/crpg-ref-requesttypes.html
if (event.RequestType === 'Create' || event.RequestType === 'Update') {
try {
const templateValues = JSON.parse(event.ResourceProperties.TemplateValues).templateValues;
const templateInputBucket = event.ResourceProperties.TemplateInputBucket;
const templateInputKey = event.ResourceProperties.TemplateInputKey;
const templateOutputBucket = event.ResourceProperties.TemplateOutputBucket;
const templateOutputKey = crypto.randomBytes(32).toString('hex');

const getObjectResponse = await s3Client.send(new GetObjectCommand({
Bucket: templateInputBucket,
Key: templateInputKey
}));

let template = await getObjectResponse.Body?.transformToString();

templateValues.forEach((templateValue: any) => {
template = template?.replace(
new RegExp(templateValue.id, 'g'),
templateValue.value
);
});

await s3Client.send(new PutObjectCommand({
Bucket: templateOutputBucket,
Key: templateOutputKey,
Body: template
}));

responseData = {
TemplateOutputKey: templateOutputKey
};
} catch (err) {
status = 'FAILED';
responseData = {
Error: err
};
}
}

return {
Status: status,
Reason: JSON.stringify(responseData),
PhysicalResourceId: event.PhysicalResourceId ?? context.logStreamName,
StackId: event.StackId,
RequestId: event.RequestId,
LogicalResourceId: event.LogicalResourceId,
Data: responseData,
};
};
Original file line number Diff line number Diff line change
@@ -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}`);
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
DO-NOT-DELETE

This file is used to create an aws_s3_assets.Asset that will be referenced when writing the transformed asset.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit e9c4704

Please sign in to comment.