AWS API Gateway Mapping Templates - VTL complications with escaping for eventbridge

632 views Asked by At

Brief:

I have an API Gateway(V1-RestAPI) that sends events to EventBrdige on HTTP calls. I would like to put an event into EventBridge using the basic template of API Gateway - Method Request Passthrough in the following way:

Entries: [{
Detail: { Data: <Method Request Passthrough Template>, MetaData {<Whatever> }},
Source: <...>,
DetailType: <...>,
EventBusName: <...>
}]

In order to do so, I have to use both

#set($context.requestOverride.header.X-Amz-Target = "AWSEvents.PutEvents")
#set($context.requestOverride.header.Content-Type = "application/x-amz-json-1.1")

The Problem:

My issue is that x-amz-json/EventBridge requires to escape characters inside the Detail Json, which isn't very fun.

If I try to use Detail: $util.escapeJavaScript({Data: ...}) it won't work because there's a usage of #foreach inside the template, and stringifying it won't do any good.

For example:

#foreach($type in $allParams.keySet())
   #set($params = $allParams.get($type)) ...

So far, I have managed to make it work only by using a very ugly solution in which I escaped the characters manually. I would like to find a way to make it look decent and "Editable" by other people.

Here is the VTL - Doesn't work but looks good

#set($context.requestOverride.header.X-Amz-Target = "AWSEvents.PutEvents")
#set($context.requestOverride.header.Content-Type = "application/x-amz-json-1.1")

#set($allParams = $input.params())
{
"Entries": [
  {
    "Detail": { 
        "Data": {
            "body-json" : $util.escapeJavaScript($input.json('$')),
            "params" : {
                #foreach($type in $allParams.keySet())
                    #set($params = $allParams.get($type))
                "$type" : {
                    #foreach($paramName in $params.keySet())
                    "$paramName" : "$util.escapeJavaScript($params.get($paramName))"
                        #if($foreach.hasNext),#end
                    #end
                }
                    #if($foreach.hasNext),#end
                #end
            },
            "stage-variables" : {
                #foreach($key in $stageVariables.keySet())
                "$key" : "$util.escapeJavaScript($stageVariables.get($key))"
                    #if($foreach.hasNext),#end
                #end
            },
            "context" : {
                "account-id" : "$context.identity.accountId",
                "api-id" : "$context.apiId",
                "api-key" : "$context.identity.apiKey",
                "authorizer-principal-id" : "$context.authorizer.principalId",
                "caller" : "$context.identity.caller",
                "cognito-authentication-provider" : "$context.identity.cognitoAuthenticationProvider",
                "cognito-authentication-type" : "$context.identity.cognitoAuthenticationType",
                "cognito-identity-id" : "$context.identity.cognitoIdentityId",
                "cognito-identity-pool-id" : "$context.identity.cognitoIdentityPoolId",
                "http-method" : "$context.httpMethod",
                "stage" : "$context.stage",
                "source-ip" : "$context.identity.sourceIp",
                "user" : "$context.identity.user",
                "user-agent" : "$context.identity.userAgent",
                "user-arn" : "$context.identity.userArn",
                "request-id" : "$context.requestId",
                "resource-id" : "$context.resourceId",
                "resource-path" : "$context.resourcePath"
            }
        },
        "MetaData": {}
    },
    "DetailType": "FILL-NAME",
    "EventBusName": "$stageVariables.eventBus",
    "Source": "ClientRequest"
  }
]
}

And here is the escaped detail version that looks like hell, but works

{ \n        \"Data\": {\n            \"body-json\" : $util.escapeJavaScript($input.json('$')),\n            \"params\" : {\n                #foreach($type in $allParams.keySet())\n                    #set($params = $allParams.get($type))\n                \"$type\" : {\n                    #foreach($paramName in $params.keySet())\n                    \"$paramName\" : \"$util.escapeJavaScript($params.get($paramName))\"\n                        #if($foreach.hasNext),#end\n                    #end\n                }\n                    #if($foreach.hasNext),#end\n                #end\n            },\n            \"stage-variables\" : {\n                #foreach($key in $stageVariables.keySet())\n                \"$key\" : \"$util.escapeJavaScript($stageVariables.get($key))\"\n                    #if($foreach.hasNext),#end\n                #end\n            },\n            \"context\" : {\n                \"account-id\" : \"$context.identity.accountId\",\n                \"api-id\" : \"$context.apiId\",\n                \"api-key\" : \"$context.identity.apiKey\",\n                \"authorizer-principal-id\" : \"$context.authorizer.principalId\",\n                \"caller\" : \"$context.identity.caller\",\n                \"cognito-authentication-provider\" : \"$context.identity.cognitoAuthenticationProvider\",\n                \"cognito-authentication-type\" : \"$context.identity.cognitoAuthenticationType\",\n                \"cognito-identity-id\" : \"$context.identity.cognitoIdentityId\",\n                \"cognito-identity-pool-id\" : \"$context.identity.cognitoIdentityPoolId\",\n                \"http-method\" : \"$context.httpMethod\",\n                \"stage\" : \"$context.stage\",\n                \"source-ip\" : \"$context.identity.sourceIp\",\n                \"user\" : \"$context.identity.user\",\n                \"user-agent\" : \"$context.identity.userAgent\",\n                \"user-arn\" : \"$context.identity.userArn\",\n                \"request-id\" : \"$context.requestId\",\n                \"resource-id\" : \"$context.resourceId\",\n                \"resource-path\" : \"$context.resourcePath\"\n            }\n        },\n        \"MetaData\": {}\n    }

As I'm not a professional in VTL or anything of that sort, if anyone has any insight as to how this can be solved, it would be awesome!

Thank you!

1

There are 1 answers

0
Omri Goldberger On

Solved it by using variables outside of the directive scope.

#set($context.requestOverride.header.X-Amz-Target = "AWSEvents.PutEvents")
#set($context.requestOverride.header.Content-Type = "application/x-amz-json-1.1")

#set($allParams = $input.params())
#set($p_params = "{
    #foreach($type in $allParams.keySet())
        #set($tmp = $allParams.get($type))
    ""$type"" : {
        #foreach($paramName in $params.keySet())
        ""$paramName"" : ""$tmp.get($paramName)""
            #if($foreach.hasNext),#end
        #end
    }
        #if($foreach.hasNext),#end
    #end
}")
#set($p_stageVariables = "{
    #foreach($key in $stageVariables.keySet())
    ""$key"" : ""$stageVariables.get($key)""
        #if($foreach.hasNext),#end
    #end
}")
#set($p_body = $input.json('$'))
#set($p_contex = "{
    ""account-id"" : ""$context.identity.accountId"",
    ""api-id"" : ""$context.apiId"",
    ""api-key"" : ""$context.identity.apiKey"",
    ""authorizer-principal-id"" : ""$context.authorizer.principalId"",
    ""caller"" : ""$context.identity.caller"",
    ""cognito-authentication-provider"" : ""$context.identity.cognitoAuthenticationProvider"",
    ""cognito-authentication-type"" : ""$context.identity.cognitoAuthenticationType"",
    ""cognito-identity-id"" : ""$context.identity.cognitoIdentityId"",
    ""cognito-identity-pool-id"" : ""$context.identity.cognitoIdentityPoolId"",
    ""http-method"" : ""$context.httpMethod"",
    ""stage"" : ""$context.stage"",
    ""source-ip"" : ""$context.identity.sourceIp"",
    ""user"" : ""$context.identity.user"",
    ""user-agent"" : ""$context.identity.userAgent"",
    ""user-arn"" : ""$context.identity.userArn"",
    ""request-id"" : ""$context.requestId"",
    ""resource-id"" : ""$context.resourceId"",
    ""resource-path"" : ""$context.resourcePath""
}")
#set($detail = "{
  ""Data"": {
      ""body-json"" : $p_body,
      ""params"" : $p_params,
      ""stage-variables"" : $p_stageVariables,
      ""context"" : $p_contex
  },
  ""MetaData"": {}}")
{
"Entries": [
  {
    "Detail": "$util.escapeJavaScript($detail).replace('\r\n', '')",
    "DetailType": "FILL-NAME",
    "EventBusName": "$stageVariables.eventBus",
    "Source": "ClientRequest"
  }
]
}