Add support for top-level JSON array in payload

Detect if leading character in JSON payload is an array bracket.  If
found, decode payload into an interface{} and then save the results into
payload["root"].  References to payload values would need to reference
the leading, "virtual" root node (i.e. "root.0.name").

Fixes #215
This commit is contained in:
Cameron Moore 2020-07-29 09:49:02 -05:00
parent f692da2465
commit 0e90ccb441
5 changed files with 127 additions and 5 deletions

View file

@ -425,6 +425,57 @@ Travis sends webhooks as `payload=<JSON_STRING>`, so the payload needs to be par
]
```
## JSON Array Payload
If the JSON payload is an array instead of an object, `webhook` will process the payload and place it into a "root" object.
Therefore, references to payload values must begin with `root.`.
For example, given the following payload (taken from the Sendgrid Event Webhook documentation):
```json
[
{
"email": "example@test.com",
"timestamp": 1513299569,
"smtp-id": "<14c5d75ce93.dfd.64b469@ismtpd-555>",
"event": "processed",
"category": "cat facts",
"sg_event_id": "sg_event_id",
"sg_message_id": "sg_message_id"
},
{
"email": "example@test.com",
"timestamp": 1513299569,
"smtp-id": "<14c5d75ce93.dfd.64b469@ismtpd-555>",
"event": "deferred",
"category": "cat facts",
"sg_event_id": "sg_event_id",
"sg_message_id": "sg_message_id",
"response": "400 try again later",
"attempt": "5"
}
]
```
A reference to the second item in the array would look like this:
```json
[
{
"id": "sendgrid",
"execute-command": "{{ .Hookecho }}",
"trigger-rule": {
"match": {
"type": "value",
"parameter": {
"source": "payload",
"name": "root.1.event"
},
"value": "deferred"
}
}
}
]
```
## XML Payload
Given the following payload:

View file

@ -168,6 +168,22 @@
],
}
},
{
"id": "sendgrid",
"execute-command": "{{ .Hookecho }}",
"command-working-directory": "/",
"response-message": "success",
"trigger-rule": {
"match": {
"type": "value",
"parameter": {
"source": "payload",
"name": "root.0.event"
},
"value": "processed"
}
}
},
{
"id": "plex",
"execute-command": "{{ .Hookecho }}",

View file

@ -97,6 +97,18 @@
name: "app.messages.message.#text"
value: "Hello!!"
- id: sendgrid
execute-command: '{{ .Hookecho }}'
command-working-directory: /
response-message: success
trigger-rule:
match:
type: value
parameter:
source: payload
name: root.0.event
value: processed
- id: plex
trigger-rule:
match:

View file

@ -16,6 +16,7 @@ import (
"path/filepath"
"strings"
"time"
"unicode"
"github.com/adnanh/webhook/internal/hook"
"github.com/adnanh/webhook/internal/middleware"
@ -141,7 +142,7 @@ func main() {
}
if *logPath != "" {
file, err := os.OpenFile(*logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
file, err := os.OpenFile(*logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o666)
if err != nil {
logQueue = append(logQueue, fmt.Sprintf("error opening log file %q: %v", *logPath, err))
// we'll bail out below
@ -385,9 +386,29 @@ func hookHandler(w http.ResponseWriter, r *http.Request) {
decoder := json.NewDecoder(bytes.NewReader(body))
decoder.UseNumber()
err := decoder.Decode(&payload)
if err != nil {
log.Printf("[%s] error parsing JSON payload %+v\n", rid, err)
var firstChar byte
for i := 0; i < len(body); i++ {
if unicode.IsSpace(rune(body[i])) {
continue
}
firstChar = body[i]
break
}
if firstChar == byte('[') {
var arrayPayload interface{}
err := decoder.Decode(&arrayPayload)
if err != nil {
log.Printf("[%s] error parsing JSON array payload %+v\n", rid, err)
}
payload = make(map[string]interface{}, 1)
payload["root"] = arrayPayload
} else {
err := decoder.Decode(&payload)
if err != nil {
log.Printf("[%s] error parsing JSON payload %+v\n", rid, err)
}
}
case strings.Contains(contentType, "x-www-form-urlencoded"):
@ -648,7 +669,7 @@ func handleHook(h *hook.Hook, rid string, headers, query, payload *map[string]in
return string(out), err
}
func writeHttpResponseCode(w http.ResponseWriter, rid string, hookId string, responseCode int) {
func writeHttpResponseCode(w http.ResponseWriter, rid, hookId string, responseCode int) {
// Check if the given return code is supported by the http package
// by testing if there is a StatusText for this code.
if len(http.StatusText(responseCode)) > 0 {

View file

@ -547,6 +547,28 @@ env: HOOK_head_commit.timestamp=2013-03-12T08:14:29-07:00
`success`,
``,
},
{
"payload-json-array",
"sendgrid",
nil,
"POST",
nil,
"application/json",
`[
{
"email": "example@test.com",
"timestamp": 1513299569,
"smtp-id": "<14c5d75ce93.dfd.64b469@ismtpd-555>",
"event": "processed",
"category": "cat facts",
"sg_event_id": "sg_event_id",
"sg_message_id": "sg_message_id"
}
]`,
http.StatusOK,
`success`,
``,
},
{
"multipart",
"plex",