Skip to content

Commit 1b15d8c

Browse files
authored
feat: support function urls (#147)
1 parent 07b32d7 commit 1b15d8c

19 files changed

+445
-237
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"conventional-changelog-cli": "^2.2.2",
2525
"conventional-github-releaser": "^3.1.5",
2626
"ospec": "^4.1.1",
27-
"sinon": "^13.0.1",
27+
"sinon": "^14.0.0",
2828
"source-map-support": "^0.5.21"
2929
},
3030
"scripts": {

src/__test__/alb.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Context } from 'aws-lambda';
22
import o from 'ospec';
3-
import { LambdaAlbRequest } from '../request.alb.js';
4-
import { AlbExample, ApiGatewayExample, clone, CloudfrontExample } from './examples.js';
3+
import { LambdaAlbRequest } from '../http/request.alb.js';
4+
import { AlbExample, ApiGatewayExample, clone, CloudfrontExample, UrlExample } from './examples.js';
55
import { fakeLog } from './log.js';
66

77
o.spec('AlbGateway', () => {
@@ -11,6 +11,7 @@ o.spec('AlbGateway', () => {
1111
o(LambdaAlbRequest.is(ApiGatewayExample)).equals(false);
1212
o(LambdaAlbRequest.is(CloudfrontExample)).equals(false);
1313
o(LambdaAlbRequest.is(AlbExample)).equals(true);
14+
o(LambdaAlbRequest.is(UrlExample)).equals(false);
1415
});
1516

1617
o('should extract headers', () => {

src/__test__/api.gateway.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Context } from 'aws-lambda';
22
import o from 'ospec';
3-
import { LambdaApiGatewayRequest } from '../request.api.gateway.js';
3+
import { LambdaApiGatewayRequest } from '../http/request.api.gateway.js';
44
import { AlbExample, ApiGatewayExample, clone, CloudfrontExample } from './examples.js';
55
import { fakeLog } from './log.js';
66

src/__test__/cloudfront.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { CloudFrontRequestEvent, Context } from 'aws-lambda';
22
import o from 'ospec';
3-
import { LambdaCloudFrontRequest } from '../request.cloudfront.js';
4-
import { AlbExample, ApiGatewayExample, clone, CloudfrontExample } from './examples.js';
3+
import { LambdaCloudFrontRequest } from '../http/request.cloudfront.js';
4+
import { AlbExample, ApiGatewayExample, clone, CloudfrontExample, UrlExample } from './examples.js';
55
import { fakeLog } from './log.js';
66

77
o.spec('CloudFront', () => {
@@ -10,6 +10,7 @@ o.spec('CloudFront', () => {
1010
o(LambdaCloudFrontRequest.is(CloudfrontExample)).equals(true);
1111
o(LambdaCloudFrontRequest.is(AlbExample)).equals(false);
1212
o(LambdaCloudFrontRequest.is(ApiGatewayExample)).equals(false);
13+
o(LambdaCloudFrontRequest.is(UrlExample)).equals(false);
1314
});
1415

1516
o('should extract headers', () => {

src/__test__/examples.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'source-map-support/register.js';
22
import { ALBEvent, APIGatewayProxyEvent, CloudFrontRequestEvent } from 'aws-lambda';
3+
import { UrlEvent } from '../http/request.function';
34

45
export const ApiGatewayExample: APIGatewayProxyEvent = {
56
body: 'eyJ0ZXN0IjoiYm9keSJ9',
@@ -154,6 +155,43 @@ export const AlbExample: ALBEvent = {
154155
isBase64Encoded: true,
155156
};
156157

158+
export const UrlExample: UrlEvent = {
159+
version: '2.0',
160+
routeKey: '$default',
161+
rawPath: '/v1/🦄/🌈/🦄.json',
162+
rawQueryString: '%F0%9F%A6%84=abc123',
163+
headers: {
164+
'x-amzn-trace-id': 'Root=1-624e71a0-114297900a437c050c74f1fe',
165+
'x-forwarded-proto': 'https',
166+
host: 'fakeId.lambda-url.ap-southeast-2.on.aws',
167+
'x-forwarded-port': '443',
168+
'x-forwarded-for': '10.88.254.254',
169+
'accept-encoding': 'br,gzip',
170+
'x-amz-cf-id': '5jJe5RyAHtE6OmIFkedddTRlFpvHYZvGIwoWNEm9YJ0OUHOFVET_Pw==',
171+
'user-agent': 'Amazon CloudFront',
172+
via: '2.0 db2406d2a95ec212c318a2e2518f9244.cloudfront.net (CloudFront)',
173+
},
174+
requestContext: {
175+
accountId: 'anonymous',
176+
apiId: 'fakeId',
177+
domainName: 'fakeId.lambda-url.ap-southeast-2.on.aws',
178+
domainPrefix: 'fakeId',
179+
http: {
180+
method: 'GET',
181+
path: '/v1/🦄/🌈/🦄.json',
182+
protocol: 'HTTP/1.1',
183+
sourceIp: '64.252.109.40',
184+
userAgent: 'Amazon CloudFront',
185+
},
186+
requestId: '6ffbc360-d84e-463c-a112-dcc6279cb4bb',
187+
routeKey: '$default',
188+
stage: '$default',
189+
time: '07/Apr/2022:05:07:44 +0000',
190+
timeEpoch: 1649308064171,
191+
},
192+
isBase64Encoded: false,
193+
};
194+
157195
export function clone<T>(c: T): T {
158196
return JSON.parse(JSON.stringify(c));
159197
}

src/__test__/readme.example.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { S3Event } from 'aws-lambda';
22
import { lf } from '../function.js';
33
import { LambdaRequest } from '../request.js';
4-
import { LambdaHttpResponse } from '../response.http.js';
4+
import { LambdaHttpResponse } from '../http/response.http.js';
55

66
export async function main(req: LambdaRequest<S3Event>): Promise<void> {
77
console.log('foo', req.id);

src/__test__/request.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { LambdaAlbRequest } from '../request.alb.js';
1+
import { LambdaAlbRequest } from '../http/request.alb.js';
22
import { AlbExample, clone } from './examples.js';
33
import { fakeLog } from './log.js';
44
import o from 'ospec';

src/__test__/url.test.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { Context } from 'aws-lambda';
2+
import o from 'ospec';
3+
import { LambdaUrlRequest } from '../http/request.function.js';
4+
import { AlbExample, ApiGatewayExample, clone, CloudfrontExample, UrlExample } from './examples.js';
5+
import { fakeLog } from './log.js';
6+
7+
o.spec('FunctionUrl', () => {
8+
const fakeContext = {} as Context;
9+
10+
o('should match the event', () => {
11+
o(LambdaUrlRequest.is(ApiGatewayExample)).equals(false);
12+
o(LambdaUrlRequest.is(CloudfrontExample)).equals(false);
13+
o(LambdaUrlRequest.is(AlbExample)).equals(false);
14+
o(LambdaUrlRequest.is(UrlExample)).equals(true);
15+
});
16+
17+
o('should extract headers', () => {
18+
const req = new LambdaUrlRequest(UrlExample, fakeContext, fakeLog);
19+
20+
o(req.header('accept-encoding')).equals('br,gzip');
21+
o(req.header('Accept-Encoding')).equals('br,gzip');
22+
});
23+
24+
o('should extract methods', () => {
25+
const req = new LambdaUrlRequest(UrlExample, fakeContext, fakeLog);
26+
o(req.method).equals('GET');
27+
});
28+
29+
o('should upper case method', () => {
30+
const newReq = clone(UrlExample);
31+
newReq.requestContext.http.method = 'post';
32+
const req = new LambdaUrlRequest(newReq, fakeContext, fakeLog);
33+
o(req.method).equals('POST');
34+
});
35+
36+
o('should extract query parameters', () => {
37+
const newReq = clone(UrlExample);
38+
newReq.rawQueryString = 'api=abc123';
39+
40+
const req = new LambdaUrlRequest(newReq, fakeContext, fakeLog);
41+
o(req.query.get('api')).deepEquals('abc123');
42+
o(req.query.getAll('api')).deepEquals(['abc123']);
43+
});
44+
45+
o('should support utf8 paths and query', () => {
46+
const req = new LambdaUrlRequest(UrlExample, fakeContext, fakeLog);
47+
o(req.path).equals('/v1/🦄/🌈/🦄.json');
48+
o(req.query.get('🦄')).equals('abc123');
49+
});
50+
});

src/__test__/wrap.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ import o from 'ospec';
44
import sinon from 'sinon';
55
import { lf } from '../function.js';
66
import { LambdaRequest } from '../request.js';
7-
import { LambdaHttpRequest } from '../request.http.js';
8-
import { LambdaHttpResponse } from '../response.http.js';
7+
import { LambdaHttpRequest } from '../http/request.http.js';
8+
import { LambdaHttpResponse } from '../http/response.http.js';
99
import { AlbExample, ApiGatewayExample, clone, CloudfrontExample } from './examples.js';
1010
import { fakeLog } from './log.js';
11-
import { HttpMethods } from '../router.js';
11+
import { HttpMethods } from '../http/router.js';
1212

1313
function assertAlbResult(x: unknown): asserts x is ALBResult {}
1414
function assertCloudfrontResult(x: unknown): asserts x is CloudFrontResultResponse {}

src/function.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@ import { LambdaResponse } from './response.js';
44
import { ApplicationJson, HttpHeader, HttpHeaderAmazon, HttpHeaderRequestId } from './header.js';
55
import { LogType } from './log.js';
66
import { LambdaRequest } from './request.js';
7-
import { LambdaAlbRequest } from './request.alb.js';
8-
import { LambdaApiGatewayRequest } from './request.api.gateway.js';
9-
import { LambdaCloudFrontRequest } from './request.cloudfront.js';
10-
import { HttpRequestEvent, HttpResponse, LambdaHttpRequest } from './request.http.js';
11-
import { LambdaHttpResponse } from './response.http.js';
12-
import { Router } from './router.js';
7+
import { LambdaAlbRequest } from './http/request.alb.js';
8+
import { LambdaApiGatewayRequest } from './http/request.api.gateway.js';
9+
import { LambdaCloudFrontRequest } from './http/request.cloudfront.js';
10+
import { HttpRequestEvent, HttpResponse, LambdaHttpRequest } from './http/request.http.js';
11+
import { LambdaHttpResponse } from './http/response.http.js';
12+
import { Router } from './http/router.js';
13+
import { LambdaUrlRequest } from './http/request.function.js';
1314

1415
export interface HttpStatus {
1516
statusCode: string;
@@ -119,6 +120,7 @@ export class lf {
119120

120121
static request(req: HttpRequestEvent, ctx: Context, log: LogType): LambdaHttpRequest {
121122
if (LambdaAlbRequest.is(req)) return new LambdaAlbRequest(req, ctx, log);
123+
if (LambdaUrlRequest.is(req)) return new LambdaUrlRequest(req, ctx, log);
122124
if (LambdaApiGatewayRequest.is(req)) return new LambdaApiGatewayRequest(req, ctx, log);
123125
if (LambdaCloudFrontRequest.is(req)) return new LambdaCloudFrontRequest(req, ctx, log);
124126
throw new Error('Request is not a a ALB, ApiGateway or Cloudfront event');

0 commit comments

Comments
 (0)