mirror of
https://github.com/adnanh/webhook.git
synced 2025-05-14 01:24:54 +00:00
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:
parent
f692da2465
commit
0e90ccb441
5 changed files with 127 additions and 5 deletions
|
@ -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
|
## XML Payload
|
||||||
|
|
||||||
Given the following payload:
|
Given the following payload:
|
||||||
|
|
|
@ -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",
|
"id": "plex",
|
||||||
"execute-command": "{{ .Hookecho }}",
|
"execute-command": "{{ .Hookecho }}",
|
||||||
|
|
|
@ -97,6 +97,18 @@
|
||||||
name: "app.messages.message.#text"
|
name: "app.messages.message.#text"
|
||||||
value: "Hello!!"
|
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
|
- id: plex
|
||||||
trigger-rule:
|
trigger-rule:
|
||||||
match:
|
match:
|
||||||
|
|
25
webhook.go
25
webhook.go
|
@ -16,6 +16,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
"github.com/adnanh/webhook/internal/hook"
|
"github.com/adnanh/webhook/internal/hook"
|
||||||
"github.com/adnanh/webhook/internal/middleware"
|
"github.com/adnanh/webhook/internal/middleware"
|
||||||
|
@ -141,7 +142,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if *logPath != "" {
|
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 {
|
if err != nil {
|
||||||
logQueue = append(logQueue, fmt.Sprintf("error opening log file %q: %v", *logPath, err))
|
logQueue = append(logQueue, fmt.Sprintf("error opening log file %q: %v", *logPath, err))
|
||||||
// we'll bail out below
|
// we'll bail out below
|
||||||
|
@ -385,10 +386,30 @@ func hookHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
decoder := json.NewDecoder(bytes.NewReader(body))
|
decoder := json.NewDecoder(bytes.NewReader(body))
|
||||||
decoder.UseNumber()
|
decoder.UseNumber()
|
||||||
|
|
||||||
|
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)
|
err := decoder.Decode(&payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[%s] error parsing JSON payload %+v\n", rid, err)
|
log.Printf("[%s] error parsing JSON payload %+v\n", rid, err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case strings.Contains(contentType, "x-www-form-urlencoded"):
|
case strings.Contains(contentType, "x-www-form-urlencoded"):
|
||||||
fd, err := url.ParseQuery(string(body))
|
fd, err := url.ParseQuery(string(body))
|
||||||
|
@ -648,7 +669,7 @@ func handleHook(h *hook.Hook, rid string, headers, query, payload *map[string]in
|
||||||
return string(out), err
|
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
|
// Check if the given return code is supported by the http package
|
||||||
// by testing if there is a StatusText for this code.
|
// by testing if there is a StatusText for this code.
|
||||||
if len(http.StatusText(responseCode)) > 0 {
|
if len(http.StatusText(responseCode)) > 0 {
|
||||||
|
|
|
@ -547,6 +547,28 @@ env: HOOK_head_commit.timestamp=2013-03-12T08:14:29-07:00
|
||||||
`success`,
|
`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",
|
"multipart",
|
||||||
"plex",
|
"plex",
|
||||||
|
|
Loading…
Add table
Reference in a new issue