diff --git a/runtime-tests/lambda/index.test.ts b/runtime-tests/lambda/index.test.ts index 8da3afc3c..e5b06de19 100644 --- a/runtime-tests/lambda/index.test.ts +++ b/runtime-tests/lambda/index.test.ts @@ -1,6 +1,17 @@ import { Readable } from 'stream' -import { handle, streamHandle } from '../../src/adapter/aws-lambda/handler' -import type { LambdaEvent } from '../../src/adapter/aws-lambda/handler' +import { + ALBProcessor, + EventV1Processor, + EventV2Processor, + 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 +978,53 @@ describe('streamHandle function', () => { expect(output).toContain(jsonResponsePrelude.replace(nullSequence, '')) }) }) + +describe('getProcessor function', () => { + it('Should return ALBProcessor 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).toBeInstanceOf(ALBProcessor) + }) + + it('Should return EventV1Processor 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).toBeInstanceOf(EventV1Processor) + }) + + it('Should return EventV2Processor 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).toBeInstanceOf(EventV2Processor) + }) +}) diff --git a/src/adapter/aws-lambda/handler.ts b/src/adapter/aws-lambda/handler.ts index 634545e43..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 @@ -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 => {