From 0be531f09378cdf6bfe3eaacd1eb1eed85df73c7 Mon Sep 17 00:00:00 2001 From: Omkar Patil Date: Tue, 19 Nov 2024 07:43:50 -0500 Subject: [PATCH 1/3] Add alb event null check for testing convenience --- src/adapter/aws-lambda/handler.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/adapter/aws-lambda/handler.ts b/src/adapter/aws-lambda/handler.ts index 634545e43..fc9199f5e 100644 --- a/src/adapter/aws-lambda/handler.ts +++ b/src/adapter/aws-lambda/handler.ts @@ -407,7 +407,7 @@ class ALBProcessor extends EventProcessor { protected getQueryString(event: ALBProxyEvent): string { // In the case of ALB Integration either queryStringParameters or multiValueQueryStringParameters can be present not both - /* + /* In other cases like when using the serverless framework, the event object does contain both queryStringParameters and multiValueQueryStringParameters: Below is an example event object for this URL: /payment/b8c55e69?select=amount&select=currency { @@ -471,7 +471,10 @@ export const getProcessor = (event: LambdaEvent): EventProcessor => } const isProxyEventALB = (event: LambdaEvent): event is ALBProxyEvent => { - return Object.hasOwn(event.requestContext, 'elb') + if (event.requestContext) { + return Object.hasOwn(event.requestContext, 'elb') + } + return false } const isProxyEventV2 = (event: LambdaEvent): event is APIGatewayProxyEventV2 => { From 482a2b36fd37da73907267716c25a14da99b6094 Mon Sep 17 00:00:00 2001 From: Omkar Patil Date: Sat, 23 Nov 2024 12:42:28 -0500 Subject: [PATCH 2/3] Add tests for getProcessor function --- runtime-tests/lambda/index.test.ts | 58 ++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/runtime-tests/lambda/index.test.ts b/runtime-tests/lambda/index.test.ts index 8da3afc3c..f31ad0ae6 100644 --- a/runtime-tests/lambda/index.test.ts +++ b/runtime-tests/lambda/index.test.ts @@ -1,6 +1,10 @@ import { Readable } from 'stream' -import { handle, streamHandle } from '../../src/adapter/aws-lambda/handler' -import type { LambdaEvent } from '../../src/adapter/aws-lambda/handler' +import { getProcessor, handle, streamHandle } from '../../src/adapter/aws-lambda/handler' +import type { + ALBProxyEvent, + APIGatewayProxyEventV2, + LambdaEvent, +} from '../../src/adapter/aws-lambda/handler' import type { ApiGatewayRequestContext, ApiGatewayRequestContextV2, @@ -967,3 +971,53 @@ describe('streamHandle function', () => { expect(output).toContain(jsonResponsePrelude.replace(nullSequence, '')) }) }) + +describe('getProcessor function', () => { + it('Should return a Processor (ALB) for an ALBProxyEvent event', () => { + const event: ALBProxyEvent = { + httpMethod: 'GET', + path: '/', + body: null, + isBase64Encoded: false, + requestContext: { + elb: { + targetGroupArn: + 'arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/lambda-279XGJDqGZ5rsrHC2Fjr/49e9d65c45c6791a', + }, + }, + } + + const processor = getProcessor(event) + expect(processor).toBeDefined() + }) + + it('Should return a Processor (V1) for an event without requestContext', () => { + const event = { + httpMethod: 'GET', + path: '/', + body: null, + isBase64Encoded: false, + } + + // while LambdaEvent RequestContext property is mandatory, it can be absent when testing through invoke-api or AWS Console + // in such cases, a V1 processor should be returned + const processor = getProcessor(event as unknown as LambdaEvent) + expect(processor).toBeDefined() + }) + + it('Should return a Processor (V2) for an APIGatewayProxyEventV2 event', () => { + const event: APIGatewayProxyEventV2 = { + version: '2.0', + routeKey: '$default', + headers: { 'content-type': 'text/plain' }, + rawPath: '/', + rawQueryString: '', + body: null, + isBase64Encoded: false, + requestContext: testApiGatewayRequestContextV2, + } + + const processor = getProcessor(event) + expect(processor).toBeDefined() + }) +}) From d3376e9c5a0a7102dac7e1147613584fb4f11a71 Mon Sep 17 00:00:00 2001 From: Omkar Patil Date: Sun, 24 Nov 2024 19:14:34 -0500 Subject: [PATCH 3/3] Export processor classes and add instanceOf assertions in tests --- runtime-tests/lambda/index.test.ts | 21 ++++++++++++++------- src/adapter/aws-lambda/handler.ts | 8 ++++---- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/runtime-tests/lambda/index.test.ts b/runtime-tests/lambda/index.test.ts index f31ad0ae6..e5b06de19 100644 --- a/runtime-tests/lambda/index.test.ts +++ b/runtime-tests/lambda/index.test.ts @@ -1,5 +1,12 @@ import { Readable } from 'stream' -import { getProcessor, handle, streamHandle } from '../../src/adapter/aws-lambda/handler' +import { + ALBProcessor, + EventV1Processor, + EventV2Processor, + getProcessor, + handle, + streamHandle, +} from '../../src/adapter/aws-lambda/handler' import type { ALBProxyEvent, APIGatewayProxyEventV2, @@ -973,7 +980,7 @@ describe('streamHandle function', () => { }) describe('getProcessor function', () => { - it('Should return a Processor (ALB) for an ALBProxyEvent event', () => { + it('Should return ALBProcessor for an ALBProxyEvent event', () => { const event: ALBProxyEvent = { httpMethod: 'GET', path: '/', @@ -988,10 +995,10 @@ describe('getProcessor function', () => { } const processor = getProcessor(event) - expect(processor).toBeDefined() + expect(processor).toBeInstanceOf(ALBProcessor) }) - it('Should return a Processor (V1) for an event without requestContext', () => { + it('Should return EventV1Processor for an event without requestContext', () => { const event = { httpMethod: 'GET', path: '/', @@ -1002,10 +1009,10 @@ describe('getProcessor function', () => { // while LambdaEvent RequestContext property is mandatory, it can be absent when testing through invoke-api or AWS Console // in such cases, a V1 processor should be returned const processor = getProcessor(event as unknown as LambdaEvent) - expect(processor).toBeDefined() + expect(processor).toBeInstanceOf(EventV1Processor) }) - it('Should return a Processor (V2) for an APIGatewayProxyEventV2 event', () => { + it('Should return EventV2Processor for an APIGatewayProxyEventV2 event', () => { const event: APIGatewayProxyEventV2 = { version: '2.0', routeKey: '$default', @@ -1018,6 +1025,6 @@ describe('getProcessor function', () => { } const processor = getProcessor(event) - expect(processor).toBeDefined() + expect(processor).toBeInstanceOf(EventV2Processor) }) }) diff --git a/src/adapter/aws-lambda/handler.ts b/src/adapter/aws-lambda/handler.ts index fc9199f5e..8d7bb6c18 100644 --- a/src/adapter/aws-lambda/handler.ts +++ b/src/adapter/aws-lambda/handler.ts @@ -184,7 +184,7 @@ export const handle = { +export abstract class EventProcessor { protected abstract getPath(event: E): string protected abstract getMethod(event: E): string @@ -272,7 +272,7 @@ abstract class EventProcessor { } } -class EventV2Processor extends EventProcessor { +export class EventV2Processor extends EventProcessor { protected getPath(event: APIGatewayProxyEventV2): string { return event.rawPath } @@ -315,7 +315,7 @@ class EventV2Processor extends EventProcessor { const v2Processor: EventV2Processor = new EventV2Processor() -class EventV1Processor extends EventProcessor> { +export class EventV1Processor extends EventProcessor> { protected getPath(event: Exclude): string { return event.path } @@ -375,7 +375,7 @@ class EventV1Processor extends EventProcessor { +export class ALBProcessor extends EventProcessor { protected getHeaders(event: ALBProxyEvent): Headers { const headers = new Headers() // if multiValueHeaders is present the ALB will use it instead of the headers field