Сопоставление данных с кодом 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"}
...