Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add XML payload support #376

Merged
merged 5 commits into from
Dec 25, 2019
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
language: go

go:
- 1.12.x
- 1.13.x
- master

Expand Down
50 changes: 50 additions & 0 deletions docs/Hook-Examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -424,3 +424,53 @@ Travis sends webhooks as `payload=<JSON_STRING>`, so the payload needs to be par
}
]
```

## XML Payload

Given the following payload:

```xml
<app>
<users>
<user id="1" name="Jeff" />
<user id="2" name="Sally" />
</users>
<messages>
<message id="1" from_user="1" to_user="2">Hello!!</message>
</messages>
</app>
```

```json
[
{
"id": "deploy",
"execute-command": "/root/my-server/deployment.sh",
"command-working-directory": "/root/my-server",
"trigger-rule": {
"and": [
{
"match": {
"type": "value",
"parameter": {
"source": "payload",
"name": "app.users.user.0.-name"
},
"value": "Jeff"
}
},
{
"match": {
"type": "value",
"parameter": {
"source": "payload",
"name": "app.messages.message.#text"
},
"value": "Hello!!"
}
},
],
}
}
]
```
30 changes: 29 additions & 1 deletion docs/Referencing-Request-Values.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,34 @@ There are three types of request values:

If the payload contains a key with the specified name "commits.0.commit.id", then the value of that key has priority over the dot-notation referencing.

3. XML Payload

Referencing XML payload parameters is much like the JSON examples above, but XML is more complex.
Element attributes are prefixed by a hyphen (`-`).
Element values are prefixed by a pound (`#`).

Take the following XML payload:

```xml
<app>
<users>
<user id="1" name="Jeff" />
<user id="2" name="Sally" />
</users>
<messages>
<message id="1" from_user="1" to_user="2">Hello!!</message>
</messages>
</app>
```

To access a given `user` element, you must treat them as an array.
So `app.users.user.0.name` yields `Jeff`.

Since there's only one `message` tag, it's not treated as an array.
So `app.messages.message.id` yields `1`.

To access the text within the `message` tag, you would use: `app.messages.message.#text`.

If you are referencing values for environment, you can use `envname` property to set the name of the environment variable like so
```json
{
Expand Down Expand Up @@ -87,4 +115,4 @@ and for query variables you can use
{
"source": "entire-query"
}
```
```
10 changes: 8 additions & 2 deletions docs/Webhook-Parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ Usage of webhook:
-cert string
path to the HTTPS certificate pem file (default "cert.pem")
-cipher-suites string
comma-separated list of supported TLS cipher suites
comma-separated list of supported TLS cipher suites
-debug
show debug output
-header value
response header to return, specified in format name=value, use multiple times to set multiple headers
-hooks value
Expand All @@ -16,7 +18,7 @@ Usage of webhook:
-key string
path to the HTTPS certificate private key pem file (default "key.pem")
-list-cipher-suites
list available TLS cipher suites
list available TLS cipher suites
-nopanic
do not panic if hooks cannot be loaded when webhook is not running in verbose mode
-port int
Expand All @@ -33,6 +35,10 @@ Usage of webhook:
show verbose output
-version
display webhook version and quit
-x-request-id
use X-Request-Id header, if present, as request ID
-x-request-id-limit int
truncate X-Request-Id header to limit; default no limit
```

Use any of the above specified flags to override their default behavior.
Expand Down
9 changes: 5 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ module github.com/adnanh/webhook
go 1.13

require (
github.com/codegangsta/negroni v0.2.1-0.20171009163950-5bc66cf1ad89
github.com/clbanning/mxj v1.8.4
github.com/dustin/go-humanize v1.0.0
github.com/fsnotify/fsnotify v1.4.7 // indirect
github.com/ghodss/yaml v1.0.0
github.com/go-chi/chi v4.0.2+incompatible
github.com/gofrs/uuid v3.2.0+incompatible
github.com/gorilla/context v1.1.1 // indirect
github.com/gorilla/mux v1.5.1-0.20171008214913-bdd5a5a1b0b4
github.com/gorilla/mux v1.7.3
github.com/kr/pretty v0.1.0 // indirect
golang.org/x/sys v0.0.0-20171006175012-ebfc5b463182 // indirect
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/fsnotify.v1 v1.4.2
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7 // indirect
Expand Down
23 changes: 15 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
github.com/codegangsta/negroni v0.2.1-0.20171009163950-5bc66cf1ad89 h1:+c/F2yO4QuofTrz3Liqu39jIxyz2EyTah9KqGSnVV+w=
github.com/codegangsta/negroni v0.2.1-0.20171009163950-5bc66cf1ad89/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0=
github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I=
github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-chi/chi v4.0.2+incompatible h1:maB6vn6FqCxrpz4FqWdh4+lwpyZIQS7YEAUcHlgXVRs=
github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.5.1-0.20171008214913-bdd5a5a1b0b4 h1:aBFtRbVEPqAz62oplPnuPbFSSFL6DmofeRgbizIwsVw=
github.com/gorilla/mux v1.5.1-0.20171008214913-bdd5a5a1b0b4/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
golang.org/x/sys v0.0.0-20171006175012-ebfc5b463182 h1:7cKexPAAZFbkQtOZ/08DxRPYYxWzMBesz2/gC7esAtI=
golang.org/x/sys v0.0.0-20171006175012-ebfc5b463182/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.2 h1:AwZiD/bIUttYJ+n/k1UwlSUsM+VSE6id7UAnSKqQ+Tc=
Expand Down
124 changes: 124 additions & 0 deletions internal/middleware/dumper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package middleware

// Derived from from the Goa project, MIT Licensed
// https://github.com/goadesign/goa/blob/v3/http/middleware/debug.go

import (
"bufio"
"bytes"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"sort"
"strings"

"github.com/gorilla/mux"
)

// responseDupper tees the response to a buffer and a response writer.
type responseDupper struct {
http.ResponseWriter
Buffer *bytes.Buffer
Status int
}

// Dumper returns a debug middleware which prints detailed information about
// incoming requests and outgoing responses including all headers, parameters
// and bodies.
func Dumper(w io.Writer) func(http.Handler) http.Handler {
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
buf := &bytes.Buffer{}
// Request ID
rid := r.Context().Value(RequestIDKey)

// Request URL
buf.WriteString(fmt.Sprintf("> [%s] %s %s", rid, r.Method, r.URL.String()))

// Request Headers
keys := make([]string, len(r.Header))
i := 0
for k := range r.Header {
keys[i] = k
i++
}
sort.Strings(keys)
for _, k := range keys {
buf.WriteString(fmt.Sprintf("\n> [%s] %s: %s", rid, k, strings.Join(r.Header[k], ", ")))
}

// Request parameters
params := mux.Vars(r)
keys = make([]string, len(params))
i = 0
for k := range params {
keys[i] = k
i++
}
sort.Strings(keys)
for _, k := range keys {
buf.WriteString(fmt.Sprintf("\n> [%s] %s: %s", rid, k, strings.Join(r.Header[k], ", ")))
}

// Request body
b, err := ioutil.ReadAll(r.Body)
if err != nil {
b = []byte("failed to read body: " + err.Error())
}
if len(b) > 0 {
buf.WriteByte('\n')
lines := strings.Split(string(b), "\n")
for _, line := range lines {
buf.WriteString(fmt.Sprintf("> [%s] %s\n", rid, line))
}
}
r.Body = ioutil.NopCloser(bytes.NewBuffer(b))

dupper := &responseDupper{ResponseWriter: rw, Buffer: &bytes.Buffer{}}
h.ServeHTTP(dupper, r)

buf.WriteString(fmt.Sprintf("\n< [%s] %s", rid, http.StatusText(dupper.Status)))
keys = make([]string, len(dupper.Header()))
i = 0
for k := range dupper.Header() {
keys[i] = k
i++
}
sort.Strings(keys)
for _, k := range keys {
buf.WriteString(fmt.Sprintf("\n< [%s] %s: %s", rid, k, strings.Join(dupper.Header()[k], ", ")))
}
if dupper.Buffer.Len() > 0 {
buf.WriteByte('\n')
lines := strings.Split(dupper.Buffer.String(), "\n")
for _, line := range lines {
buf.WriteString(fmt.Sprintf("< [%s] %s\n", rid, line))
}
}
buf.WriteByte('\n')
w.Write(buf.Bytes())
})
}
}

// Write writes the data to the buffer and connection as part of an HTTP reply.
func (r *responseDupper) Write(b []byte) (int, error) {
r.Buffer.Write(b)
return r.ResponseWriter.Write(b)
}

// WriteHeader records the status and sends an HTTP response header with status code.
func (r *responseDupper) WriteHeader(s int) {
r.Status = s
r.ResponseWriter.WriteHeader(s)
}

// Hijack supports the http.Hijacker interface.
func (r *responseDupper) Hijack() (net.Conn, *bufio.ReadWriter, error) {
if hijacker, ok := r.ResponseWriter.(http.Hijacker); ok {
return hijacker.Hijack()
}
return nil, nil, fmt.Errorf("dumper middleware: inner ResponseWriter cannot be hijacked: %T", r.ResponseWriter)
}
59 changes: 59 additions & 0 deletions internal/middleware/logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package middleware

import (
"bytes"
"fmt"
"log"
"net/http"
"time"

"github.com/dustin/go-humanize"
"github.com/go-chi/chi/middleware"
)

// Logger is a middleware that logs useful data about each HTTP request.
type Logger struct {
Logger middleware.LoggerInterface
}

// NewLogger creates a new RequestLogger Handler.
func NewLogger() func(next http.Handler) http.Handler {
return middleware.RequestLogger(&Logger{})
}

// NewLogEntry creates a new LogEntry for the request.
func (l *Logger) NewLogEntry(r *http.Request) middleware.LogEntry {
e := &LogEntry{
req: r,
buf: &bytes.Buffer{},
}

return e
}

// LogEntry represents an individual log entry.
type LogEntry struct {
*Logger
req *http.Request
buf *bytes.Buffer
}

// Write constructs and writes the final log entry.
func (l *LogEntry) Write(status, bytes int, elapsed time.Duration) {
rid := GetReqID(l.req.Context())
if rid != "" {
fmt.Fprintf(l.buf, "[%s] ", rid)
}

fmt.Fprintf(l.buf, "%03d | %s | %s | ", status, humanize.IBytes(uint64(bytes)), elapsed)
l.buf.WriteString(l.req.Host + " | " + l.req.Method + " " + l.req.RequestURI)
log.Print(l.buf.String())
}

/// Panic prints the call stack for a panic.
func (l *LogEntry) Panic(v interface{}, stack []byte) {
e := l.NewLogEntry(l.req).(*LogEntry)
fmt.Fprintf(e.buf, "panic: %#v", v)
log.Print(e.buf.String())
log.Print(string(stack))
}
Loading