Skip to content
This repository has been archived by the owner on Jun 13, 2023. It is now read-only.

Commit

Permalink
feat(api-gateway): support v2 http api (#106)
Browse files Browse the repository at this point in the history
  • Loading branch information
ophiryael authored Sep 13, 2021
1 parent 3f47e6c commit 1647b03
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 47 deletions.
156 changes: 110 additions & 46 deletions epsagon/lambda_trigger.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,6 @@ import (

type triggerFactory func(event interface{}, metadataOnly bool) *protocol.Event

func unknownTrigger(event interface{}, metadataOnly bool) *protocol.Event {
return &protocol.Event{}
}

func getReflectType(i interface{}) reflect.Type {
return reflect.TypeOf(i)
}

func mapParametersToString(params map[string]string) string {
buf, err := json.Marshal(params)
if err != nil {
Expand All @@ -43,50 +35,106 @@ func mapParametersToString(params map[string]string) string {
return string(buf)
}

func triggerAPIGatewayProxyRequest(rawEvent interface{}, metadataOnly bool) *protocol.Event {
event, ok := rawEvent.(lambdaEvents.APIGatewayProxyRequest)
if !ok {
tracer.AddException(&protocol.Exception{
Type: "trigger-creation",
Message: fmt.Sprintf(
"failed to convert rawEvent to lambdaEvents.APIGatewayProxyRequest %v",
rawEvent),
Time: tracer.GetTimestamp(),
})
return nil
type APIGatewayEventFields struct {
requestId string
headers map[string]string
host string
httpMethod string
stage string
queryStringParameters map[string]string
pathParameters map[string]string
path string
body string
}

func getEventAssertionException(rawEvent interface{}, assertedType string) *protocol.Exception {
return &protocol.Exception{
Type: "trigger-creation",
Message: fmt.Sprintf("failed to convert rawEvent to %s. %v", assertedType, rawEvent),
Time: tracer.GetTimestamp(),
}
triggerEvent := &protocol.Event{
Id: event.RequestContext.RequestID,
}

func getAPIGatewayTriggerEvent(eventFields *APIGatewayEventFields, metadataOnly bool) *protocol.Event {
triggerEvent := getAPIGatewayBaseEvent(eventFields)
if !metadataOnly {
addAPIGatewayRequestData(triggerEvent, eventFields)
}
return triggerEvent
}

func getAPIGatewayBaseEvent(eventFields *APIGatewayEventFields) *protocol.Event {
return &protocol.Event{
Id: eventFields.requestId,
Origin: "trigger",
StartTime: tracer.GetTimestamp(),
Resource: &protocol.Resource{
Name: event.Headers["Host"],
Name: eventFields.host,
Type: "api_gateway",
Operation: event.HTTPMethod,
Operation: eventFields.httpMethod,
Metadata: map[string]string{
"stage": event.RequestContext.Stage,
"query_string_parameters": mapParametersToString(event.QueryStringParameters),
"path_parameters": mapParametersToString(event.PathParameters),
"path": event.Resource,
"stage": eventFields.stage,
"query_string_parameters": mapParametersToString(eventFields.queryStringParameters),
"path_parameters": mapParametersToString(eventFields.pathParameters),
"path": eventFields.path,
},
},
}
if !metadataOnly {
if bodyJSON, err := json.Marshal(event.Body); err != nil {
tracer.AddException(&protocol.Exception{
Type: "trigger-creation",
Message: fmt.Sprintf("Failed to serialize body %s", event.Body),
Traceback: string(debug.Stack()),
Time: tracer.GetTimestamp(),
})
triggerEvent.Resource.Metadata["body"] = ""
} else {
triggerEvent.Resource.Metadata["body"] = string(bodyJSON)
}
triggerEvent.Resource.Metadata["headers"] = mapParametersToString(event.Headers)
}

func addAPIGatewayRequestData(triggerEvent *protocol.Event, eventFields *APIGatewayEventFields) {
if bodyJSON, err := json.Marshal(eventFields.body); err != nil {
tracer.AddException(&protocol.Exception{
Type: "trigger-creation",
Message: fmt.Sprintf("Failed to serialize body %s", eventFields.body),
Traceback: string(debug.Stack()),
Time: tracer.GetTimestamp(),
})
triggerEvent.Resource.Metadata["body"] = ""
} else {
triggerEvent.Resource.Metadata["body"] = string(bodyJSON)
}
triggerEvent.Resource.Metadata["headers"] = mapParametersToString(eventFields.headers)
}

return triggerEvent
func triggerAPIGatewayProxyRequest(rawEvent interface{}, metadataOnly bool) *protocol.Event {
event, ok := rawEvent.(lambdaEvents.APIGatewayProxyRequest)
if !ok {
assertionException := getEventAssertionException(rawEvent, "lambdaEvents.APIGatewayProxyRequest")
tracer.AddException(assertionException)
return nil
}
return getAPIGatewayTriggerEvent(&APIGatewayEventFields{
requestId: event.RequestContext.RequestID,
headers: event.Headers,
host: event.Headers["Host"],
httpMethod: event.HTTPMethod,
stage: event.RequestContext.Stage,
queryStringParameters: event.QueryStringParameters,
pathParameters: event.PathParameters,
path: event.Resource,
body: event.Body,
}, metadataOnly)
}

func triggerAPIGatewayV2HTTPRequest(rawEvent interface{}, metadataOnly bool) *protocol.Event {
event, ok := rawEvent.(lambdaEvents.APIGatewayV2HTTPRequest)
if !ok {
assertionException := getEventAssertionException(rawEvent, "lambdaEvents.APIGatewayV2HTTPRequest")
tracer.AddException(assertionException)
return nil
}
return getAPIGatewayTriggerEvent(&APIGatewayEventFields{
requestId: event.RequestContext.RequestID,
headers: event.Headers,
host: event.Headers["host"],
httpMethod: event.RequestContext.HTTP.Method,
stage: event.RequestContext.Stage,
queryStringParameters: event.QueryStringParameters,
pathParameters: event.PathParameters,
path: event.RawPath,
body: event.Body,
}, metadataOnly)
}

func triggerS3Event(rawEvent interface{}, metadataOnly bool) *protocol.Event {
Expand Down Expand Up @@ -351,6 +399,10 @@ var (
EventType: reflect.TypeOf(lambdaEvents.APIGatewayProxyRequest{}),
Factory: triggerAPIGatewayProxyRequest,
},
"api_gateway_http2": {
EventType: reflect.TypeOf(lambdaEvents.APIGatewayV2HTTPRequest{}),
Factory: triggerAPIGatewayV2HTTPRequest,
},
"aws:s3": {
EventType: reflect.TypeOf(lambdaEvents.S3Event{}),
Factory: triggerS3Event,
Expand Down Expand Up @@ -394,12 +446,22 @@ type recordField struct {
EventSource string
}

type httpDescription struct {
Method string
}

type requestContext struct {
APIID string
HTTP httpDescription
}

type interestingFields struct {
Records []recordField
HTTPMethod string
Context map[string]interface{}
MethodArn string
Source string
Records []recordField
HTTPMethod string
Context map[string]interface{}
MethodArn string
Source string
RequestContext requestContext
}

func guessTriggerSource(payload json.RawMessage) string {
Expand All @@ -421,6 +483,8 @@ func guessTriggerSource(payload json.RawMessage) string {
triggerSource = "api_gateway"
} else if _, ok := rawEvent.Context["http-method"]; ok {
triggerSource = "api_gateway_no_proxy"
} else if len(rawEvent.RequestContext.APIID) > 0 && len(rawEvent.RequestContext.HTTP.Method) > 0 {
triggerSource = "api_gateway_http2"
} else if len(rawEvent.Source) > 0 {
sourceSlice := strings.Split(rawEvent.Source, ".")
triggerSource = sourceSlice[len(sourceSlice)-1]
Expand Down
35 changes: 34 additions & 1 deletion epsagon/lambda_trigger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,29 @@ var (
"hello": "world",
},
}
exampleAPIGatewayV2HTTP = lambdaEvents.APIGatewayV2HTTPRequest{
RawPath: "/hello",
RequestContext: lambdaEvents.APIGatewayV2HTTPRequestContext{
APIID: "test-api",
HTTP: lambdaEvents.APIGatewayV2HTTPRequestContextHTTPDescription{
Method: "GET",
},
},
Body: "<b>hello world</b>",
IsBase64Encoded: false,
Headers: map[string]string{
"hello": "world",
},
StageVariables: map[string]string{
"hello": "world",
},
PathParameters: map[string]string{
"hello": "world",
},
QueryStringParameters: map[string]string{
"hello": "world",
},
}
exampleDDB = lambdaEvents.DynamoDBEvent{
Records: []lambdaEvents.DynamoDBEventRecord{
lambdaEvents.DynamoDBEventRecord{
Expand Down Expand Up @@ -86,7 +109,7 @@ var _ = Describe("epsagon trigger suite", func() {
})

Context("Handling of known trigger - API Gateway", func() {
It("Identifies the first known handler, API Gateway", func() {
It("Identifies the first known handler, API Gateway - REST", func() {
exampleJSON, err := json.Marshal(exampleAPIGateWay)
if err != nil {
Fail("Failed to marshal json")
Expand All @@ -95,6 +118,16 @@ var _ = Describe("epsagon trigger suite", func() {
Expect(len(events)).To(BeNumerically("==", 1))
Expect(events[0].Resource.Type).To(Equal("api_gateway"))
})

It("Identifies the first known handler, API Gateway - HTTP", func() {
exampleJSON, err := json.Marshal(exampleAPIGatewayV2HTTP)
if err != nil {
Fail("Failed to marshal json")
}
addLambdaTrigger(json.RawMessage(exampleJSON), false, triggerFactories, tracer.GlobalTracer)
Expect(len(events)).To(BeNumerically("==", 1))
Expect(events[0].Resource.Type).To(Equal("api_gateway"))
})
})
Context("Handling of known trigger - DynamoDB", func() {
It("Identifies the first known handler, DynamoDB", func() {
Expand Down

0 comments on commit 1647b03

Please sign in to comment.