Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 50 additions & 4 deletions src/openapi-lambda-adapters.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { APIGatewayProxyStructuredResultV2 } from 'aws-lambda'
import { APIGatewayProxyStructuredResultV2, Context } from 'aws-lambda'
import { AxiosRequestConfig } from 'axios'
import { AxiosError, HttpMethod, Operation } from 'openapi-client-axios'
import { convertAxiosToApiGw, convertApiGwToAxios } from './openapi-lambda-adapters'
Expand Down Expand Up @@ -26,7 +26,8 @@ describe('Adapt axios request/response to AWS Lambda Proxy Event/Response', () =
// then
const event = convertAxiosToApiGw(axiosConfig, operation)
expect(event.rawPath).toEqual('/v1/users')
expect(Object.keys(event.headers).length).toEqual(2)
expect(Object.keys(event.headers)).toContain('Accept')
expect(Object.keys(event.headers)).toContain('authorization')
expect(event.pathParameters).toEqual({})
expect(event.queryStringParameters).toEqual({})
expect(event.rawQueryString).toEqual('')
Expand Down Expand Up @@ -204,7 +205,52 @@ describe('Adapt axios request/response to AWS Lambda Proxy Event/Response', () =
expect(event.headers['x-null']).toBeUndefined()
expect(event.headers['x-undefined']).toBeUndefined()
})


it('defaults to lambda arn the user agent when passed in the context', () => {
// given
const axiosConfig: AxiosRequestConfig = {
method: 'get',
url: '/v1/users',
}
const operation: Operation = {
path: '/v1/users',
method: HttpMethod.Get,
responses: {}
}

// then
const event = convertAxiosToApiGw(axiosConfig, operation, {
invokedFunctionArn: 'arn:aws:lambda:us-east-1:123456789012:function:my-function'
} as Context)

expect(event.requestContext.authorizer.lambda)
expect(event.requestContext.http.userAgent).toBe('lambda-invoke-arn:aws:lambda:us-east-1:123456789012:function:my-function')
expect(event.headers['User-Agent']).toBe('lambda-invoke-arn:aws:lambda:us-east-1:123456789012:function:my-function')
})

it('includes the user agent when passed in the request config', () => {
// given
const axiosConfig: AxiosRequestConfig = {
method: 'get',
url: '/v1/users',
headers: {
'User-Agent': 'daniel-api',
}
}
const operation: Operation = {
path: '/v1/users',
method: HttpMethod.Get,
responses: {}
}

// then
const event = convertAxiosToApiGw(axiosConfig, operation, {
invokedFunctionArn: 'arn:aws:lambda:us-east-1:123456789012:function:my-function'
} as Context)

expect(event.requestContext.http.userAgent).toBe('daniel-api')
expect(event.headers['User-Agent']).toBe('daniel-api')
})
})

describe('Api GW Proxy Response to Axios Response', () => {
Expand Down Expand Up @@ -431,4 +477,4 @@ describe('Adapt axios request/response to AWS Lambda Proxy Event/Response', () =

})

})
})
29 changes: 21 additions & 8 deletions src/openapi-lambda-adapters.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
import type {
APIGatewayProxyEventV2, APIGatewayProxyStructuredResultV2,
APIGatewayProxyEventQueryStringParameters, APIGatewayProxyEventPathParameters,
APIGatewayProxyEventV2WithLambdaAuthorizer,
Context
} from 'aws-lambda'

Expand Down Expand Up @@ -30,7 +31,9 @@ const lambdaRunner = async (axiosConfig: AxiosRequestConfig, operation: Operatio
.then((resp) => convertApiGwToAxios(resp, axiosConfig))
}

export const convertAxiosToApiGw = (config: AxiosRequestConfig, operation: Operation, crtLambdaContext?: Context): APIGatewayProxyEventV2 => {
export interface LambdaRunnerAuthContext { 'lambda-invoke': true, callerIdentity: string }

export const convertAxiosToApiGw = (config: AxiosRequestConfig, operation: Operation, crtLambdaContext?: Context): APIGatewayProxyEventV2WithLambdaAuthorizer<LambdaRunnerAuthContext> => {
// extract path params
// eg: for path template /v1/users/{id} & path url /v1/users/1108 -> will extract {'id': '1108'}
const template = operation.path
Expand Down Expand Up @@ -61,7 +64,15 @@ export const convertAxiosToApiGw = (config: AxiosRequestConfig, operation: Opera
headers[key] = val.toString()
}

const lambdaPayload = {
// identify caller lambda
const sourceIdentity = ['lambda-invoke', crtLambdaContext?.invokedFunctionArn].filter(Boolean).join('-')

// default to lambda-invoke user-agent
if (!headers['User-Agent'] && !headers['user-agent']) {
headers['User-Agent'] = sourceIdentity
}

const lambdaPayload: APIGatewayProxyEventV2WithLambdaAuthorizer<LambdaRunnerAuthContext> = {
version: '2.0',
routeKey: '$default',
rawPath: config.url,
Expand All @@ -75,7 +86,7 @@ export const convertAxiosToApiGw = (config: AxiosRequestConfig, operation: Opera
authorizer: {
lambda: {
'lambda-invoke': true,
callerIdentity: crtLambdaContext?.invokedFunctionArn ?? 'lambda-invoke-not-specified'
callerIdentity: sourceIdentity
}
},
domainName: 'lambda-invoke',
Expand All @@ -85,7 +96,7 @@ export const convertAxiosToApiGw = (config: AxiosRequestConfig, operation: Opera
sourceIp: '',
path: config.url,
protocol: 'HTTP/1.1',
userAgent: 'lambda-invoke'
userAgent: headers['user-agent'] ?? headers['User-Agent']
},
requestId: crtLambdaContext?.awsRequestId ?? `lambda-invoke-${uuidv4()}`,
routeKey: '$default',
Expand All @@ -94,9 +105,11 @@ export const convertAxiosToApiGw = (config: AxiosRequestConfig, operation: Opera
timeEpoch: Date.now()
},
body: config.data ? JSON.stringify(config.data) : '',
isBase64Encoded: false,
httpMethod: config.method
} as APIGatewayProxyEventV2
isBase64Encoded: false
}

// for backwards compat with older event format
Object.assign(lambdaPayload, { httpMethod: config.method })

debug('lambdaRequest %o', lambdaPayload)

Expand Down
Loading