From 0cd26c38c36c7d83579fa619443d1b224c9aa730 Mon Sep 17 00:00:00 2001 From: Andrew Taylor Date: Fri, 6 Sep 2024 11:51:29 -0700 Subject: [PATCH 1/4] allows passing optional prop to NodejsFunction --- packages/backend-function/src/factory.ts | 49 ++++++++++++++++++++---- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/packages/backend-function/src/factory.ts b/packages/backend-function/src/factory.ts index e32e72cfb6..cbb2508b48 100644 --- a/packages/backend-function/src/factory.ts +++ b/packages/backend-function/src/factory.ts @@ -13,7 +13,11 @@ import { SsmEnvironmentEntry, } from '@aws-amplify/plugin-types'; import { Construct } from 'constructs'; -import { NodejsFunction, OutputFormat } from 'aws-cdk-lib/aws-lambda-nodejs'; +import { + type NodejsFunctionProps as CDKNodejsFunctionProps, + NodejsFunction, + OutputFormat, +} from 'aws-cdk-lib/aws-lambda-nodejs'; import * as path from 'path'; import { Duration, Stack, Tags } from 'aws-cdk-lib'; import { CfnFunction, Runtime } from 'aws-cdk-lib/aws-lambda'; @@ -60,7 +64,7 @@ export type FunctionSchedule = TimeInterval | CronSchedule; * Entry point for defining a function in the Amplify ecosystem */ export const defineFunction = ( - props: FunctionProps = {} + props: FunctionPropsWithOptional = {} ): ConstructFactory< ResourceProvider & ResourceAccessAcceptorFactory & @@ -126,6 +130,12 @@ export type FunctionProps = { schedule?: FunctionSchedule | FunctionSchedule[]; }; +export type FunctionPropsWithOptional = Omit< + Partial, + keyof FunctionProps +> & + FunctionProps; + /** * Create Lambda functions in the context of an Amplify backend definition */ @@ -135,7 +145,7 @@ class FunctionFactory implements ConstructFactory { * Create a new AmplifyFunctionFactory */ constructor( - private readonly props: FunctionProps, + private readonly props: FunctionPropsWithOptional, private readonly callerStack?: string ) {} @@ -161,7 +171,7 @@ class FunctionFactory implements ConstructFactory { ): HydratedFunctionProps => { const name = this.resolveName(); resourceNameValidator?.validate(name); - return { + const hydratedProps = { name, entry: this.resolveEntry(), timeoutSeconds: this.resolveTimeout(), @@ -170,6 +180,10 @@ class FunctionFactory implements ConstructFactory { runtime: this.resolveRuntime(), schedule: this.resolveSchedule(), }; + return { + ...this.props, + ...hydratedProps, + }; }; private resolveName = () => { @@ -277,12 +291,17 @@ class FunctionFactory implements ConstructFactory { } type HydratedFunctionProps = Required; +type HydratedFunctionPropsWithOptional = Omit< + Partial, + keyof FunctionProps +> & + HydratedFunctionProps; class FunctionGenerator implements ConstructContainerEntryGenerator { readonly resourceGroupName = 'function'; constructor( - private readonly props: HydratedFunctionProps, + private readonly props: HydratedFunctionPropsWithOptional, private readonly outputStorageStrategy: BackendOutputStorageStrategy ) {} @@ -312,7 +331,7 @@ class AmplifyFunction constructor( scope: Construct, id: string, - props: HydratedFunctionProps, + props: HydratedFunctionPropsWithOptional, backendSecretResolver: BackendSecretResolver, outputStorageStrategy: BackendOutputStorageStrategy ) { @@ -355,7 +374,7 @@ class AmplifyFunction let functionLambda: NodejsFunction; try { - functionLambda = new NodejsFunction(scope, `${id}-lambda`, { + const nodejsFunctionFunctionProps: CDKNodejsFunctionProps = { entry: props.entry, timeout: Duration.seconds(props.timeoutSeconds), memorySize: props.memoryMB, @@ -371,7 +390,21 @@ class AmplifyFunction minify: true, sourceMap: true, }, - }); + }; + + // Copy all props to optionalProps to allow for deletion of keys + const optionalProps: Partial = { ...props }; + // Remove hydrated props from optional props + for (const key of Object.keys(nodejsFunctionFunctionProps)) { + delete optionalProps[key as keyof FunctionPropsWithOptional]; + } + // as CDKNodejsFunctionProps is necessary because of a type conflict with runtime + const functionProps = { + ...optionalProps, + ...nodejsFunctionFunctionProps, + } as CDKNodejsFunctionProps; + + functionLambda = new NodejsFunction(scope, `${id}-lambda`, functionProps); } catch (error) { throw new AmplifyUserError( 'NodeJSFunctionConstructInitializationError', From e9de792b0baf98ea57303028259a3cbb727f729a Mon Sep 17 00:00:00 2001 From: Andrew Taylor Date: Fri, 6 Sep 2024 12:02:25 -0700 Subject: [PATCH 2/4] Add test to allow passing retryAttempts prop to NodejsFunction via defineFunction --- packages/backend-function/src/factory.test.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/backend-function/src/factory.test.ts b/packages/backend-function/src/factory.test.ts index 316271131e..fa7ece0681 100644 --- a/packages/backend-function/src/factory.test.ts +++ b/packages/backend-function/src/factory.test.ts @@ -513,4 +513,20 @@ void describe('AmplifyFunctionFactory', () => { 'function-Lambda' ); }); + + void it('allows passing retryAttempts prop to NodejsFunction', () => { + const functionFactory = defineFunction({ + entry: './test-assets/default-lambda/handler.ts', + name: 'myCoolNodejsFunctionLambda', + retryAttempts: 2, + }); + const lambda = functionFactory.getInstance(getInstanceProps); + const stack = Stack.of(lambda.resources.lambda); + const template = Template.fromStack(stack); + template.resourceCountIs('AWS::Lambda::Function', 1); + template.resourceCountIs('AWS::Lambda::EventInvokeConfig', 1); + template.hasResourceProperties('AWS::Lambda::EventInvokeConfig', { + MaximumRetryAttempts: 2, + }); + }); }); From d832065916a904b12c3442aff46ee128340533f4 Mon Sep 17 00:00:00 2001 From: Andrew Taylor Date: Fri, 6 Sep 2024 13:18:30 -0700 Subject: [PATCH 3/4] Reference existing type --- packages/backend-function/src/factory.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/backend-function/src/factory.ts b/packages/backend-function/src/factory.ts index cbb2508b48..95a7cbdf5d 100644 --- a/packages/backend-function/src/factory.ts +++ b/packages/backend-function/src/factory.ts @@ -291,10 +291,7 @@ class FunctionFactory implements ConstructFactory { } type HydratedFunctionProps = Required; -type HydratedFunctionPropsWithOptional = Omit< - Partial, - keyof FunctionProps -> & +type HydratedFunctionPropsWithOptional = FunctionPropsWithOptional & HydratedFunctionProps; class FunctionGenerator implements ConstructContainerEntryGenerator { From 1fab1ab87ad7b9a83ff8cb8a3121aec07964d5b6 Mon Sep 17 00:00:00 2001 From: Andrew Taylor Date: Fri, 6 Sep 2024 13:22:14 -0700 Subject: [PATCH 4/4] Add changeset --- .changeset/brave-pugs-develop.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/brave-pugs-develop.md diff --git a/.changeset/brave-pugs-develop.md b/.changeset/brave-pugs-develop.md new file mode 100644 index 0000000000..7e1af8de82 --- /dev/null +++ b/.changeset/brave-pugs-develop.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-function': minor +--- + +Allow all optional props for the `NodejsFunction` CDK construct to be passed via `defineFunction`