From 950c2ba1e6fa467505557ec017685ad7c4e5d270 Mon Sep 17 00:00:00 2001 From: Bryan Moffatt Date: Wed, 6 Apr 2022 14:52:34 -0700 Subject: [PATCH 1/4] Add Lambda Function URL HTTP request and response types --- events/lambda_http.go | 64 +++++++++++++++++++++++ events/lambda_http_test.go | 57 ++++++++++++++++++++ events/testdata/lambda-urls-request.json | 47 +++++++++++++++++ events/testdata/lambda-urls-response.json | 13 +++++ 4 files changed, 181 insertions(+) create mode 100644 events/lambda_http.go create mode 100644 events/lambda_http_test.go create mode 100644 events/testdata/lambda-urls-request.json create mode 100644 events/testdata/lambda-urls-response.json diff --git a/events/lambda_http.go b/events/lambda_http.go new file mode 100644 index 00000000..8401f3f1 --- /dev/null +++ b/events/lambda_http.go @@ -0,0 +1,64 @@ +// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +package events + +// LambdaHTTPRequest contains data coming from the new HTTP API Gateway +type LambdaHTTPRequest struct { + Version string `json:"version"` // Version is expected to always be `"2.0"` + RouteKey string `json:"routeKey"` // RouteKey is expected to always be `"$default"` + RawPath string `json:"rawPath"` + RawQueryString string `json:"rawQueryString"` + Cookies []string `json:"cookies,omitempty"` + Headers map[string]string `json:"headers"` + QueryStringParameters map[string]string `json:"queryStringParameters,omitempty"` + RequestContext LambdaHTTPRequestContext `json:"requestContext"` + Body string `json:"body,omitempty"` + IsBase64Encoded bool `json:"isBase64Encoded"` +} + +// LambdaHTTPRequestContext contains the information to identify the AWS account and resources invoking the Lambda function. +type LambdaHTTPRequestContext struct { + RouteKey string `json:"routeKey"` // RouteKey is expected to always be `"$default"` + AccountID string `json:"accountId"` + Stage string `json:"stage"` // Stage is expected to always be `"$default"` + RequestID string `json:"requestId"` + Authorizer *LambdaHTTPRequestContextAuthorizerDescription `json:"authorizer,omitempty"` + APIID string `json:"apiId"` // APIID is the Lambda URL ID + DomainName string `json:"domainName"` // DomainName is of the format `".lambda-url..on.aws"` + DomainPrefix string `json:"domainPrefix"` // DomainPrefix is the Lambda URL ID + Time string `json:"time"` + TimeEpoch int64 `json:"timeEpoch"` + HTTP LambdaHTTPRequestContextHTTPDescription `json:"http"` +} + +// LambdaHTTPRequestContextAuthorizerDescription contains authorizer information for the request context. +type LambdaHTTPRequestContextAuthorizerDescription struct { + IAM *LambdaHTTPRequestContextAuthorizerIAMDescription `json:"iam,omitempty"` +} + +// LambdaHTTPRequestContextAuthorizerIAMDescription contains IAM information for the request context. +type LambdaHTTPRequestContextAuthorizerIAMDescription struct { + AccessKey string `json:"accessKey"` + AccountID string `json:"accountId"` + CallerID string `json:"callerId"` + UserARN string `json:"userArn"` + UserID string `json:"userId"` +} + +// LambdaHTTPRequestContextHTTPDescription contains HTTP information for the request context. +type LambdaHTTPRequestContextHTTPDescription struct { + Method string `json:"method"` + Path string `json:"path"` + Protocol string `json:"protocol"` + SourceIP string `json:"sourceIp"` + UserAgent string `json:"userAgent"` +} + +// LambdaHTTPResponse configures the response to be returned by API Gateway V2 for the request +type LambdaHTTPResponse struct { + StatusCode int `json:"statusCode"` + Headers map[string]string `json:"headers"` + Body string `json:"body"` + IsBase64Encoded bool `json:"isBase64Encoded"` + Cookies []string `json:"cookies"` +} diff --git a/events/lambda_http_test.go b/events/lambda_http_test.go new file mode 100644 index 00000000..1ed220a9 --- /dev/null +++ b/events/lambda_http_test.go @@ -0,0 +1,57 @@ +// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +package events + +import ( + "encoding/json" + "io/ioutil" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestLambdaHTTPResponseMarshaling(t *testing.T) { + + // read json from file + inputJSON, err := ioutil.ReadFile("./testdata/lambda-urls-response.json") + if err != nil { + t.Errorf("could not open test file. details: %v", err) + } + + // de-serialize into Go object + var inputEvent LambdaHTTPResponse + if err := json.Unmarshal(inputJSON, &inputEvent); err != nil { + t.Errorf("could not unmarshal event. details: %v", err) + } + + // serialize to json + outputJSON, err := json.Marshal(inputEvent) + if err != nil { + t.Errorf("could not marshal event. details: %v", err) + } + + assert.JSONEq(t, string(inputJSON), string(outputJSON)) +} + +func TestLambdaHTTPRequestMarshaling(t *testing.T) { + + // read json from file + inputJSON, err := ioutil.ReadFile("./testdata/lambda-urls-request.json") + if err != nil { + t.Errorf("could not open test file. details: %v", err) + } + + // de-serialize into Go object + var inputEvent LambdaHTTPRequest + if err := json.Unmarshal(inputJSON, &inputEvent); err != nil { + t.Errorf("could not unmarshal event. details: %v", err) + } + + // serialize to json + outputJSON, err := json.Marshal(inputEvent) + if err != nil { + t.Errorf("could not marshal event. details: %v", err) + } + + assert.JSONEq(t, string(inputJSON), string(outputJSON)) +} diff --git a/events/testdata/lambda-urls-request.json b/events/testdata/lambda-urls-request.json new file mode 100644 index 00000000..39c48a88 --- /dev/null +++ b/events/testdata/lambda-urls-request.json @@ -0,0 +1,47 @@ +{ + "version": "2.0", + "routeKey": "$default", + "rawPath": "/my/path", + "rawQueryString": "parameter1=value1¶meter1=value2¶meter2=value", + "cookies": [ + "cookie1", + "cookie2" + ], + "headers": { + "header1": "value1", + "header2": "value1,value2" + }, + "queryStringParameters": { + "parameter1": "value1,value2", + "parameter2": "value" + }, + "requestContext": { + "accountId": "123456789012", + "apiId": "", + "authorizer": { + "iam": { + "accessKey": "AKIA...", + "accountId": "111122223333", + "callerId": "AIDA...", + "userArn": "arn:aws:iam::111122223333:user/example-user", + "userId": "AIDA..." + } + }, + "domainName": ".lambda-url.us-west-2.on.aws", + "domainPrefix": "", + "http": { + "method": "POST", + "path": "/my/path", + "protocol": "HTTP/1.1", + "sourceIp": "123.123.123.123", + "userAgent": "agent" + }, + "requestId": "id", + "routeKey": "$default", + "stage": "$default", + "time": "12/Mar/2020:19:03:58 +0000", + "timeEpoch": 1583348638390 + }, + "body": "Hello from client!", + "isBase64Encoded": false +} diff --git a/events/testdata/lambda-urls-response.json b/events/testdata/lambda-urls-response.json new file mode 100644 index 00000000..64fc7fea --- /dev/null +++ b/events/testdata/lambda-urls-response.json @@ -0,0 +1,13 @@ +{ + "statusCode": 201, + "headers": { + "Content-Type": "application/json", + "My-Custom-Header": "Custom Value" + }, + "body": "{ \"message\": \"Hello, world!\" }", + "cookies": [ + "Cookie_1=Value1; Expires=21 Oct 2021 07:48 GMT", + "Cookie_2=Value2; Max-Age=78000" + ], + "isBase64Encoded": false +} From d3f2417c0d8326324d6df0ea75fe986b13b1da02 Mon Sep 17 00:00:00 2001 From: Bryan Moffatt Date: Thu, 7 Apr 2022 10:07:55 -0700 Subject: [PATCH 2/4] naming update, remove fields documented to always be --- events/lambda_http.go | 65 +++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/events/lambda_http.go b/events/lambda_http.go index 8401f3f1..36be4fbe 100644 --- a/events/lambda_http.go +++ b/events/lambda_http.go @@ -2,42 +2,39 @@ package events -// LambdaHTTPRequest contains data coming from the new HTTP API Gateway -type LambdaHTTPRequest struct { - Version string `json:"version"` // Version is expected to always be `"2.0"` - RouteKey string `json:"routeKey"` // RouteKey is expected to always be `"$default"` - RawPath string `json:"rawPath"` - RawQueryString string `json:"rawQueryString"` - Cookies []string `json:"cookies,omitempty"` - Headers map[string]string `json:"headers"` - QueryStringParameters map[string]string `json:"queryStringParameters,omitempty"` - RequestContext LambdaHTTPRequestContext `json:"requestContext"` - Body string `json:"body,omitempty"` - IsBase64Encoded bool `json:"isBase64Encoded"` +// LambdaFunctionURLRequest contains data coming from the HTTP request to a Lambda Function URL. +type LambdaFunctionURLRequest struct { + Version string `json:"version"` // Version is expected to be `"2.0"` + RawPath string `json:"rawPath"` + RawQueryString string `json:"rawQueryString"` + Cookies []string `json:"cookies,omitempty"` + Headers map[string]string `json:"headers"` + QueryStringParameters map[string]string `json:"queryStringParameters,omitempty"` + RequestContext LambdaFunctionURLRequestContext `json:"requestContext"` + Body string `json:"body,omitempty"` + IsBase64Encoded bool `json:"isBase64Encoded"` } -// LambdaHTTPRequestContext contains the information to identify the AWS account and resources invoking the Lambda function. -type LambdaHTTPRequestContext struct { - RouteKey string `json:"routeKey"` // RouteKey is expected to always be `"$default"` - AccountID string `json:"accountId"` - Stage string `json:"stage"` // Stage is expected to always be `"$default"` - RequestID string `json:"requestId"` - Authorizer *LambdaHTTPRequestContextAuthorizerDescription `json:"authorizer,omitempty"` - APIID string `json:"apiId"` // APIID is the Lambda URL ID - DomainName string `json:"domainName"` // DomainName is of the format `".lambda-url..on.aws"` - DomainPrefix string `json:"domainPrefix"` // DomainPrefix is the Lambda URL ID - Time string `json:"time"` - TimeEpoch int64 `json:"timeEpoch"` - HTTP LambdaHTTPRequestContextHTTPDescription `json:"http"` +// LambdaFunctionURLRequestContext contains the information to identify the AWS account and resources invoking the Lambda function. +type LambdaFunctionURLRequestContext struct { + AccountID string `json:"accountId"` + RequestID string `json:"requestId"` + Authorizer *LambdaFunctionURLRequestContextAuthorizerDescription `json:"authorizer,omitempty"` + APIID string `json:"apiId"` // APIID is the Lambda URL ID + DomainName string `json:"domainName"` // DomainName is of the format `".lambda-url..on.aws"` + DomainPrefix string `json:"domainPrefix"` // DomainPrefix is the Lambda URL ID + Time string `json:"time"` + TimeEpoch int64 `json:"timeEpoch"` + HTTP LambdaFunctionURLRequestContextHTTPDescription `json:"http"` } -// LambdaHTTPRequestContextAuthorizerDescription contains authorizer information for the request context. -type LambdaHTTPRequestContextAuthorizerDescription struct { - IAM *LambdaHTTPRequestContextAuthorizerIAMDescription `json:"iam,omitempty"` +// LambdaFunctionURLRequestContextAuthorizerDescription contains authorizer information for the request context. +type LambdaFunctionURLRequestContextAuthorizerDescription struct { + IAM *LambdaFunctionURLRequestContextAuthorizerIAMDescription `json:"iam,omitempty"` } -// LambdaHTTPRequestContextAuthorizerIAMDescription contains IAM information for the request context. -type LambdaHTTPRequestContextAuthorizerIAMDescription struct { +// LambdaFunctionURLRequestContextAuthorizerIAMDescription contains IAM information for the request context. +type LambdaFunctionURLRequestContextAuthorizerIAMDescription struct { AccessKey string `json:"accessKey"` AccountID string `json:"accountId"` CallerID string `json:"callerId"` @@ -45,8 +42,8 @@ type LambdaHTTPRequestContextAuthorizerIAMDescription struct { UserID string `json:"userId"` } -// LambdaHTTPRequestContextHTTPDescription contains HTTP information for the request context. -type LambdaHTTPRequestContextHTTPDescription struct { +// LambdaFunctionURLRequestContextHTTPDescription contains HTTP information for the request context. +type LambdaFunctionURLRequestContextHTTPDescription struct { Method string `json:"method"` Path string `json:"path"` Protocol string `json:"protocol"` @@ -54,8 +51,8 @@ type LambdaHTTPRequestContextHTTPDescription struct { UserAgent string `json:"userAgent"` } -// LambdaHTTPResponse configures the response to be returned by API Gateway V2 for the request -type LambdaHTTPResponse struct { +// LambdaFunctionURLResponse configures the HTTP response to be returned by Lambda Function URL for the request. +type LambdaFunctionURLResponse struct { StatusCode int `json:"statusCode"` Headers map[string]string `json:"headers"` Body string `json:"body"` From 0578e3271e32e7943d40aaf052f51d9654715de0 Mon Sep 17 00:00:00 2001 From: Bryan Moffatt Date: Thu, 7 Apr 2022 10:13:12 -0700 Subject: [PATCH 3/4] update testdata --- .../{lambda_http.go => lambda_function_urls.go} | 0 ...http_test.go => lambda_function_urls_test.go} | 8 ++++---- events/testdata/lambda-urls-request.json | 16 +++++++--------- 3 files changed, 11 insertions(+), 13 deletions(-) rename events/{lambda_http.go => lambda_function_urls.go} (100%) rename events/{lambda_http_test.go => lambda_function_urls_test.go} (86%) diff --git a/events/lambda_http.go b/events/lambda_function_urls.go similarity index 100% rename from events/lambda_http.go rename to events/lambda_function_urls.go diff --git a/events/lambda_http_test.go b/events/lambda_function_urls_test.go similarity index 86% rename from events/lambda_http_test.go rename to events/lambda_function_urls_test.go index 1ed220a9..17c511d8 100644 --- a/events/lambda_http_test.go +++ b/events/lambda_function_urls_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestLambdaHTTPResponseMarshaling(t *testing.T) { +func TestLambdaFunctionURLResponseMarshaling(t *testing.T) { // read json from file inputJSON, err := ioutil.ReadFile("./testdata/lambda-urls-response.json") @@ -19,7 +19,7 @@ func TestLambdaHTTPResponseMarshaling(t *testing.T) { } // de-serialize into Go object - var inputEvent LambdaHTTPResponse + var inputEvent LambdaFunctionURLResponse if err := json.Unmarshal(inputJSON, &inputEvent); err != nil { t.Errorf("could not unmarshal event. details: %v", err) } @@ -33,7 +33,7 @@ func TestLambdaHTTPResponseMarshaling(t *testing.T) { assert.JSONEq(t, string(inputJSON), string(outputJSON)) } -func TestLambdaHTTPRequestMarshaling(t *testing.T) { +func TestLambdaFunctionURLRequestMarshaling(t *testing.T) { // read json from file inputJSON, err := ioutil.ReadFile("./testdata/lambda-urls-request.json") @@ -42,7 +42,7 @@ func TestLambdaHTTPRequestMarshaling(t *testing.T) { } // de-serialize into Go object - var inputEvent LambdaHTTPRequest + var inputEvent LambdaFunctionURLRequest if err := json.Unmarshal(inputJSON, &inputEvent); err != nil { t.Errorf("could not unmarshal event. details: %v", err) } diff --git a/events/testdata/lambda-urls-request.json b/events/testdata/lambda-urls-request.json index 39c48a88..f7b38ded 100644 --- a/events/testdata/lambda-urls-request.json +++ b/events/testdata/lambda-urls-request.json @@ -19,13 +19,13 @@ "accountId": "123456789012", "apiId": "", "authorizer": { - "iam": { - "accessKey": "AKIA...", - "accountId": "111122223333", - "callerId": "AIDA...", - "userArn": "arn:aws:iam::111122223333:user/example-user", - "userId": "AIDA..." - } + "iam": { + "accessKey": "AKIA...", + "accountId": "111122223333", + "callerId": "AIDA...", + "userArn": "arn:aws:iam::111122223333:user/example-user", + "userId": "AIDA..." + } }, "domainName": ".lambda-url.us-west-2.on.aws", "domainPrefix": "", @@ -37,8 +37,6 @@ "userAgent": "agent" }, "requestId": "id", - "routeKey": "$default", - "stage": "$default", "time": "12/Mar/2020:19:03:58 +0000", "timeEpoch": 1583348638390 }, From 99d1594afd577297c905e3f21b4cadea567a7562 Mon Sep 17 00:00:00 2001 From: Bryan Moffatt Date: Thu, 7 Apr 2022 10:58:01 -0700 Subject: [PATCH 4/4] updated testdata --- events/testdata/lambda-urls-request.json | 1 - 1 file changed, 1 deletion(-) diff --git a/events/testdata/lambda-urls-request.json b/events/testdata/lambda-urls-request.json index f7b38ded..c947bd60 100644 --- a/events/testdata/lambda-urls-request.json +++ b/events/testdata/lambda-urls-request.json @@ -1,6 +1,5 @@ { "version": "2.0", - "routeKey": "$default", "rawPath": "/my/path", "rawQueryString": "parameter1=value1¶meter1=value2¶meter2=value", "cookies": [