6
votes

Using AWS's API Gateway I configured an api resource as an AWS proxy to Kinesis' PutRecords action. The API consumers send me a list of events I need to forward those events to a Kinesis stream.

The format in which they send the data to the API gateway look something similar to this. It contains 1 top level element that's of type Array. The object type of each array item is a JSON document:

{
    "events":[
        {
            "time":"2017-01-01T11:43:21",
            "type":"ItemSelected",
            "application":"iOS Build 3654"
        },{
            "time":"2017-01-01:11:55:32",
            "type":"ItemSelected",
            "application":"iOS Build 3654"
        }
    ]
}

What's needed is to break each separate event into a Kinesis record and send it to as a base64Encoded string to Kinesis.

Using a Body Mapping Template I've configured the following.

{
    "StreamName":"MemberApiAuditLog",
    "Records":[
        #foreach($elem in $input.path('$.events')){
        #set($countVal=$foreach.count-1)
            "Data":"$util.base64Encode($input.json('$.events[$countVal]'))",
            "PartitionKey":"$input.path('$.memberid')"
        }
        #end
    ]
}

The problem I'm having is that the Mapping Template doesn't seem to have an issue with this the $countVal variable in this code: '$.events[$countVal]'. It somehow just doesn't recognize the $countVal. If I replace $countVal with 0, it works just fine.

I need to use $input.json(x) because the mapping template doesn't provide a different way to stringify a json object.

Questions:

  1. Other than $input.json(x) is there a way to stringify a json object in a Body Mapping Template?
    1. I've tried JSON.stringify(object), but that didn't work.
  2. How can I get the code to recognize the value of countVal in that expression? If that can be resolved, the issue will be solved.
2

2 Answers

6
votes

I've manage to make the template work, creating a valid json object.

{
    "streamName": "MemberApiAuditLog",
    "Records": [
        #set($inputRoot = $input.path('$.events'))
        #foreach($elem in $inputRoot) {
           #set($json = $input.json("$[$foreach.index]"))
            "Data":"$util.base64Encode($json)",
        #end
    ]
}

It works for me, hope for you too

-2
votes

You do not need to convert the objects into strings in the foreach loop. Instead, you should be able to simply pass the object to the base64Encode function which automatically converts it into a base64 string.

The following mapping template produced the output below:

{
    "StreamName":"MemberApiAuditLog",
    "Records":[
        #foreach($elem in $input.path('$.events')){
            "Data":"$util.base64Encode($elem)",
            "PartitionKey":"$input.path('$.memberid')"
        }
        #end
    ]
}

The output:

{
    "StreamName":"MemberApiAuditLog",
    "Records":[
        {
            "Data":"e3RpbWU9MjAxNy0wMS0wMVQxMTo0MzoyMSwgdHlwZT1JdGVtU2VsZWN0ZWQsIGFwcGxpY2F0aW9uPWlPUyBCdWlsZCAzNjU0fQ==",
            "PartitionKey":"my-member-id"
        }
        {
            "Data":"e3RpbWU9MjAxNy0wMS0wMToxMTo1NTozMiwgdHlwZT1JdGVtU2VsZWN0ZWQsIGFwcGxpY2F0aW9uPWlPUyBCdWlsZCAzNjU0fQ==",
            "PartitionKey":"my-member-id"
        }
    ]
}

And when you decode the first base64 string again, you should get this result:

{time=2017-01-01T11:43:21, type=ItemSelected, application=iOS Build 3654}

Please let me know if that worked for you.