Сопоставление данных с кодом x-www-form-urlencoded с форматом Firehose в API Gateway
Основываясь на документах, сообщениях на форуме и т. Д., Я придумал это решение, чтобы сначала преобразовать данные публикации формы, закодированные в URL-адресе, в переменную, а затем закодировать ее в base64 перед отправкой в Firehose (firehose нуждается в полезной нагрузке в этом формате)
#set($data = {})
#foreach( $token in $input.path('$').split('&') )
#set( $keyVal = $token.split('=') )
#set( $keyValSize = $keyVal.size() )
#if( $keyValSize >= 1 )
#set( $key = $util.urlDecode($keyVal[0]) )
#if( $keyValSize >= 2 )
#set( $val = $util.urlDecode($keyVal[1]) )
#else
#set( $val = '' )
#end
#end
$util.qr($data.put("$key", "$val"))
#end
{
"DeliveryStreamName": "my-firehose",
"Record": {
"Data": "$util.base64encode($data)"
},
"PartitionKey": "1"
}
Однако результат после преобразования
{
"DeliveryStreamName": "my-firehose",
"Record": {
"Data": ""
},
"PartitionKey": "1"
}
Я пробовал различные варианты $util.qr($data.put("$key", "$val"))
но, похоже, никто не работает
$util.qr($data.put("$key", $val))
$util.qr($data.put("$key", $util.parseJson($val)))
$util.qr($data.put("$key", $util.toJson($util.parseJson($val))))
$util.qr($data.put("$key", "abc")) // hardcoded just to debug
Но все они приводят к пустому Data
блок в окончательном выводе.
А этот даже не трансформируется (бросает 500)
#set($data = {
#foreach( $token in $input.path('$').split('&') )
#set( $keyVal = $token.split('=') )
#set( $keyValSize = $keyVal.size() )
#if( $keyValSize >= 1 )
#set( $key = $util.urlDecode($keyVal[0]) )
#if( $keyValSize >= 2 )
#set( $val = $util.urlDecode($keyVal[1]) )
#else
#set( $val = '' )
#end
#end
"$key": "$val" #if($foreach.hasNext),#end
#end
})
{
"DeliveryStreamName": "my-firehose",
"Record": {
"Data": "$util.base64encode($data)"
},
"PartitionKey": "1"
}
Что я напортачил?
Обновления
Основываясь на указателях @michael-sqlbot, я нашел волшебный рецепт (хотя и не полный рецепт)
#set($data = {})
#foreach( $token in $input.path('$').split('&') )
#set( $keyVal = $token.split('=') )
#set( $keyValSize = $keyVal.size() )
#if( $keyValSize >= 1 )
#set( $key = $util.urlDecode($keyVal[0]) )
#if( $keyValSize >= 2 )
#set( $val = $util.urlDecode($keyVal[1]) )
#else
#set( $val = '' )
#end
#end
$!data.put("$key", "$util.parseJson($val)")
#end
{
"DeliveryStreamName": "my-firehose",
"Record": {
"Data": "$util.base64Encode($data)"
},
"PartitionKey": "1"
}
Также, base64encode
должно быть base64Encode
. С этими изменениями я вижу поток данных. Остается только проблема в том, что это не совсем JSON:
"data": "{abc={user_info={session_id=}, event_id=77841543625, date_time=2019-12-16T21:26:17.911Z}, sb=, hello=world}"
Он также не цитирует строки должным образом, так что, возможно, есть еще кое-что, что нужно сделать.
1 ответ
Мне потребовалось немного подтолкнуть Майкла - sqlbot, чтобы направить меня в правильном направлении, поэтому спасибо за это.
В моем первоначальном подходе несколько вещей были не совсем правильными:
- API Gateway имеет меньший набор помощников, чем что-то вроде AppSync. Он поддерживает только методы, которые описаны здесь: API Gateway WebSocket API Mapping Template Reference
- Где-то там скрыта ссылка на язык шаблонов скорости Apache (VTL), который приведет вас на страницу справки по языку шаблонов скорости.
- Документ VTL рекомендует вам ознакомиться с его Руководством пользователя, и сразу неясно, поддерживает ли AWS только язык или Engine. Ответ, после МНОГО проб и ошибок, заключается в том, что он поддерживает микс.
- Плохая новость в том, что я не могу использовать такие вещи, как
util.qr
илиutil.toJson
но для моих целей я смог собрать решение, которое работает для моих целей. - Объекты / карта скорости - это не то же самое, что объекты JavaScript. Когда вы используете
$myVar.put(...)
вы получаете объект, который сериализуется во что-то вроде{ key=Value, deepKey={nestedKey=nestedValue}
. Он идентичен объекту JS, если вы замените=
подписать с:
. Чтобы сделать его похожим на JSON, вам также нужно будет правильно указать ключи и значения. Как вы понимаете, это может быстро стать некрасивым.
Я был довольно близок к тому, чтобы отказаться от этого подхода и использовать лямбду, которая может выполнять преобразования и записывать записи в Firehose вместо этого, но мои данные в кодировке URL были JSON внизу, что помогло в этом случае.
Итак, без лишних слов, вот решение, которое сработало для меня:
#set($data = "")
## parse through url encoded data, split into kv pairs
#foreach( $token in $input.path('$').split('&') )
#set( $keyVal = $token.split('=') )
#set( $keyValSize = $keyVal.size() )
#if( $keyValSize >= 1 )
#set( $key = $util.urlDecode($keyVal[0]) )
#if( $keyValSize >= 2 )
#set( $val = $util.urlDecode($keyVal[1]) )
#else
#set( $val = '' )
#end
#end
## append to stringified JSON string
#set($data = "${data}\""${key}\"":$util.escapeJavaScript($val)#if($foreach.hasNext),#end")
#end
#set($data = "$data.replaceAll('\\', '')}
")
{
"DeliveryStreamName": "my-firehose",
"Record": {
"Data": "$util.base64Encode($data)"
},
"PartitionKey": "1"
}
Объяснение
Из-за ограничений, упомянутых выше, я отказался от своего прежнего подхода и вместо этого решил сам создать строковый JSON. Здесь это делается.
Чтобы получить правильную строку, которую можно сериализовать, вам придется избегать двойных кавычек - $util.escapeJavaScript($val)
делает именно это.
#set($data = "${data}\""${key}\"":$util.escapeJavaScript($val)#if($foreach.hasNext),#end")
Вам придется обернуть key
в двойных кавычках и экранировать их, иначе результирующая строка будет неверной. \""${key}\""
делает это. Выяснение, как сбежать\"
мне потребовалось еще несколько попыток, поскольку AWS не поддерживает Velocity Escape Tool. Правильная последовательность - это двойные двойные кавычки (чтобы избежать двойных кавычек внутри выражения) и одинарная косая черта (выделение кода будет указывать на неработающее строковое выражение, поэтому я попробовал с\\
но оказалось, что подсветка синтаксиса была неправильной. Подойдет всего одна косая черта). В результате получится такая строка:
"{\"abc\":{\"user_info\":{\"session_id\":\"\"},\"event_id\":\"77841543625\",\"date_time\":\"2019-12-16T21:26:17.911Z\",\"sb\":1,\"hello\":\"world\"}"
Затем я удаляю косые черты из окончательного JSON, добавляю фигурные скобки и добавляю новую строку к каждой записи:
#set($data = "{$data.replaceAll('\\', '')} // <- wrapping braces and slash removal
") // <- this is newline adding block, not a typo
Наконец, передается полезная нагрузка Firehose:
{
"DeliveryStreamName": "my-firehose",
"Record": {
"Data": "$util.base64Encode($data)"
},
"PartitionKey": "1"
}
Это приводит к таким записям для конечного пункта назначения:
{"abc":{"user_info":{"session_id": ""}, "event_id": "77841543625", "date_time":"2019-12-16T21:26:17.911Z"}, "sb": 1, "id": "home"}
{"abc":{"user_info":{"session_id": ""}, "event_id": "84154343625", "date_time":"2019-12-16T22:31:43.543Z"}, "sb": 1, "id": "sub"}
...