Files
remark42/backend/app/notify/webhook.go
T

114 lines
2.7 KiB
Go

package notify
import (
"bytes"
"context"
"fmt"
"io/ioutil"
"net/http"
"strings"
"text/template"
log "github.com/go-pkgz/lgr"
"github.com/pkg/errors"
)
const (
webhookDefaultTemplate = `{"text": "{{.Text}}"}`
)
// WebhookClient defines an interface of client for webhook
type WebhookClient interface {
Do(*http.Request) (*http.Response, error)
}
// WebhookParams contain settings for webhook notifications
type WebhookParams struct {
WebhookURL string
Template string
Headers []string
}
// Webhook implements notify.Destination for Webhook notifications
type Webhook struct {
WebhookParams
webhookClient WebhookClient
webhookTemplate *template.Template
}
// NewWebhook makes Webhook
func NewWebhook(client WebhookClient, params WebhookParams) (*Webhook, error) {
res := &Webhook{WebhookParams: params}
if res.WebhookURL == "" {
return nil, errors.New("webhook URL is required for webhook notifications")
}
if res.Template == "" {
res.Template = webhookDefaultTemplate
}
payloadTmpl, err := template.New("webhook").Parse(res.Template)
if err != nil {
return nil, errors.Wrap(err, "unable to parse webhook template")
}
res.webhookClient = client
res.webhookTemplate = payloadTmpl
log.Printf("[DEBUG] create new webhook notifier for %s", res.WebhookURL)
return res, nil
}
// Send sends Webhook notification
func (t *Webhook) Send(ctx context.Context, req Request) error {
var payload bytes.Buffer
err := t.webhookTemplate.Execute(&payload, req.Comment)
if err != nil {
return errors.Wrap(err, "unable to compile webhook template")
}
httpReq, err := http.NewRequestWithContext(ctx, "POST", t.WebhookURL, &payload)
if err != nil {
return errors.Wrap(err, "unable to create webhook request")
}
for _, h := range t.Headers {
elems := strings.Split(h, ":")
if len(elems) != 2 {
continue
}
httpReq.Header.Set(strings.TrimSpace(elems[0]), strings.TrimSpace(elems[1]))
}
resp, err := t.webhookClient.Do(httpReq)
if err != nil {
return errors.Wrap(err, "webhook request failed")
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
errMsg := fmt.Sprintf("webhook request failed with non-OK status code: %d", resp.StatusCode)
respBody, e := ioutil.ReadAll(resp.Body)
if e != nil {
return fmt.Errorf(errMsg)
}
return fmt.Errorf("%s, body: %s", errMsg, respBody)
}
log.Printf("[DEBUG] send webhook notification, comment id %s", req.Comment.ID)
return nil
}
// SendVerification is not implemented for Webhook
func (t *Webhook) SendVerification(_ context.Context, _ VerificationRequest) error {
return nil
}
// String describes the webhook instance
func (t *Webhook) String() string {
return fmt.Sprintf("webhook notification to %s", t.WebhookURL)
}