diff --git a/events/lambda_function_urls.go b/events/lambda_function_urls.go new file mode 100644 index 00000000..36be4fbe --- /dev/null +++ b/events/lambda_function_urls.go @@ -0,0 +1,61 @@ +// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +package events + +// 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"` +} + +// 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"` +} + +// LambdaFunctionURLRequestContextAuthorizerDescription contains authorizer information for the request context. +type LambdaFunctionURLRequestContextAuthorizerDescription struct { + IAM *LambdaFunctionURLRequestContextAuthorizerIAMDescription `json:"iam,omitempty"` +} + +// LambdaFunctionURLRequestContextAuthorizerIAMDescription contains IAM information for the request context. +type LambdaFunctionURLRequestContextAuthorizerIAMDescription struct { + AccessKey string `json:"accessKey"` + AccountID string `json:"accountId"` + CallerID string `json:"callerId"` + UserARN string `json:"userArn"` + UserID string `json:"userId"` +} + +// LambdaFunctionURLRequestContextHTTPDescription contains HTTP information for the request context. +type LambdaFunctionURLRequestContextHTTPDescription struct { + Method string `json:"method"` + Path string `json:"path"` + Protocol string `json:"protocol"` + SourceIP string `json:"sourceIp"` + UserAgent string `json:"userAgent"` +} + +// 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"` + IsBase64Encoded bool `json:"isBase64Encoded"` + Cookies []string `json:"cookies"` +} diff --git a/events/lambda_function_urls_test.go b/events/lambda_function_urls_test.go new file mode 100644 index 00000000..17c511d8 --- /dev/null +++ b/events/lambda_function_urls_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 TestLambdaFunctionURLResponseMarshaling(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 LambdaFunctionURLResponse + 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 TestLambdaFunctionURLRequestMarshaling(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 LambdaFunctionURLRequest + 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..c947bd60 --- /dev/null +++ b/events/testdata/lambda-urls-request.json @@ -0,0 +1,44 @@ +{ + "version": "2.0", + "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", + "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 +}