Skip to content

Commit

Permalink
fix(adapter/aws-lambda): add alb event requestContext undefined check…
Browse files Browse the repository at this point in the history
… for testing convenience (#3691)

* Add alb event null check for testing convenience

* Add tests for getProcessor function

* Export processor classes and add instanceOf assertions in tests
  • Loading branch information
ospatil authored Nov 25, 2024
1 parent bfc190f commit 7ebf854
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 8 deletions.
65 changes: 63 additions & 2 deletions runtime-tests/lambda/index.test.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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)
})
})
15 changes: 9 additions & 6 deletions src/adapter/aws-lambda/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ export const handle = <E extends Env = Env, S extends Schema = {}, BasePath exte
}
}

abstract class EventProcessor<E extends LambdaEvent> {
export abstract class EventProcessor<E extends LambdaEvent> {
protected abstract getPath(event: E): string

protected abstract getMethod(event: E): string
Expand Down Expand Up @@ -272,7 +272,7 @@ abstract class EventProcessor<E extends LambdaEvent> {
}
}

class EventV2Processor extends EventProcessor<APIGatewayProxyEventV2> {
export class EventV2Processor extends EventProcessor<APIGatewayProxyEventV2> {
protected getPath(event: APIGatewayProxyEventV2): string {
return event.rawPath
}
Expand Down Expand Up @@ -315,7 +315,7 @@ class EventV2Processor extends EventProcessor<APIGatewayProxyEventV2> {

const v2Processor: EventV2Processor = new EventV2Processor()

class EventV1Processor extends EventProcessor<Exclude<LambdaEvent, APIGatewayProxyEventV2>> {
export class EventV1Processor extends EventProcessor<Exclude<LambdaEvent, APIGatewayProxyEventV2>> {
protected getPath(event: Exclude<LambdaEvent, APIGatewayProxyEventV2>): string {
return event.path
}
Expand Down Expand Up @@ -375,7 +375,7 @@ class EventV1Processor extends EventProcessor<Exclude<LambdaEvent, APIGatewayPro

const v1Processor: EventV1Processor = new EventV1Processor()

class ALBProcessor extends EventProcessor<ALBProxyEvent> {
export class ALBProcessor extends EventProcessor<ALBProxyEvent> {
protected getHeaders(event: ALBProxyEvent): Headers {
const headers = new Headers()
// if multiValueHeaders is present the ALB will use it instead of the headers field
Expand Down Expand Up @@ -407,7 +407,7 @@ class ALBProcessor extends EventProcessor<ALBProxyEvent> {

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
{
Expand Down Expand Up @@ -471,7 +471,10 @@ export const getProcessor = (event: LambdaEvent): EventProcessor<LambdaEvent> =>
}

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 => {
Expand Down

0 comments on commit 7ebf854

Please sign in to comment.