0

基于文档、论坛帖子等,我想出了这个解决方案,首先将 url 编码形式的帖子数据转换为变量,然后在发送到 Firehose 之前对其进行 base64 编码(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}"

它也没有正确引用字符串,所以也许还有更多的事情要做。

4

1 回答 1

1

Michael - sqlbot 稍微推动了一下,让我朝着正确的方向前进,所以谢谢你。

在我最初的方法中,有几件事不太正确:

  • API Gateway 的帮助程序比 AppSync 之类的要少。它仅支持此处记录的方法:API Gateway WebSocket API Mapping Template Reference
  • 埋在其中的某个地方还引用了Apache Velocity 模板语言 (VTL),它会将您带到 Velocity 模板语言的参考页面。
  • VTL 文档鼓励您查看其用户指南,目前尚不清楚 AWS 是否仅支持该语言或引擎。经过大量试验和错误后,答案是它支持混合。
  • 坏消息是,我不能使用类似的东西,util.qr或者util.toJson为了我的目的,我能够拼凑出一个适合我目标的解决方案。
  • Velocity Objects/Map 与 JavaScript 对象不同。当您使用时,$myVar.put(...)您会得到一个序列化为{ key=Value, deepKey={nestedKey=nestedValue}. 如果将=符号替换为 ,则它与 JS 对象相同:。为了使它像 JSON,您还必须正确引用键和值。可以想象,这会很快变得丑陋。

我非常接近放弃这种方法并使用可以进行转换并将记录写入 Firehose 的 lambda,但我的 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"}
...
于 2019-12-18T15:01:47.420 回答