summaryrefslogtreecommitdiffstats
path: root/models/webhook.go
diff options
context:
space:
mode:
authorUnknwon <u@gogs.io>2015-08-27 23:06:14 +0800
committerUnknwon <u@gogs.io>2015-08-27 23:06:14 +0800
commit23f42d92c917564435a00e8e75633b8056bd7b0d (patch)
tree6fed7f6df4c6b039e4e43eaae2fcd5788ddba3a0 /models/webhook.go
parentfc2d0e5470fa2fea260adba30866acda1aa945cb (diff)
downloadgitea-23f42d92c917564435a00e8e75633b8056bd7b0d.tar.gz
gitea-23f42d92c917564435a00e8e75633b8056bd7b0d.zip
add webhook recent deliveries
Diffstat (limited to 'models/webhook.go')
-rw-r--r--models/webhook.go220
1 files changed, 161 insertions, 59 deletions
diff --git a/models/webhook.go b/models/webhook.go
index 6211b215f2..debe46e5ec 100644
--- a/models/webhook.go
+++ b/models/webhook.go
@@ -7,21 +7,20 @@ package models
import (
"crypto/tls"
"encoding/json"
- "errors"
+ "fmt"
"io/ioutil"
+ "strings"
"sync"
"time"
+ "github.com/go-xorm/xorm"
+
"github.com/gogits/gogs/modules/httplib"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/setting"
"github.com/gogits/gogs/modules/uuid"
)
-var (
- ErrWebhookNotExist = errors.New("Webhook does not exist")
-)
-
type HookContentType int
const (
@@ -103,6 +102,11 @@ func (w *Webhook) GetSlackHook() *Slack {
return s
}
+// History returns history of webhook by given conditions.
+func (w *Webhook) History(page int) ([]*HookTask, error) {
+ return HookTasks(w.ID, page)
+}
+
// UpdateEvent handles conversion from HookEvent to Events.
func (w *Webhook) UpdateEvent() error {
data, err := json.Marshal(w.HookEvent)
@@ -124,14 +128,14 @@ func CreateWebhook(w *Webhook) error {
return err
}
-// GetWebhookById returns webhook by given ID.
-func GetWebhookById(hookId int64) (*Webhook, error) {
- w := &Webhook{ID: hookId}
- has, err := x.Get(w)
+// GetWebhookByID returns webhook by given ID.
+func GetWebhookByID(id int64) (*Webhook, error) {
+ w := new(Webhook)
+ has, err := x.Id(id).Get(w)
if err != nil {
return nil, err
} else if !has {
- return nil, ErrWebhookNotExist
+ return nil, ErrWebhookNotExist{id}
}
return w, nil
}
@@ -271,29 +275,99 @@ type Payload struct {
}
func (p Payload) GetJSONPayload() ([]byte, error) {
- data, err := json.Marshal(p)
+ data, err := json.MarshalIndent(p, "", " ")
if err != nil {
return []byte{}, err
}
return data, nil
}
+// HookRequest represents hook task request information.
+type HookRequest struct {
+ Headers map[string]string `json:"headers"`
+}
+
+// HookResponse represents hook task response information.
+type HookResponse struct {
+ Status int `json:"status"`
+ Headers map[string]string `json:"headers"`
+ Body string `json:"body"`
+}
+
// HookTask represents a hook task.
type HookTask struct {
- ID int64 `xorm:"pk autoincr"`
- RepoID int64 `xorm:"INDEX"`
- HookID int64
- Uuid string
- Type HookTaskType
- Url string
- BasePayload `xorm:"-"`
- PayloadContent string `xorm:"TEXT"`
- ContentType HookContentType
- EventType HookEventType
- IsSsl bool
- IsDelivered bool
- Delivered int64
- IsSucceed bool
+ ID int64 `xorm:"pk autoincr"`
+ RepoID int64 `xorm:"INDEX"`
+ HookID int64
+ UUID string
+ Type HookTaskType
+ URL string
+ BasePayload `xorm:"-"`
+ PayloadContent string `xorm:"TEXT"`
+ ContentType HookContentType
+ EventType HookEventType
+ IsSSL bool
+ IsDelivered bool
+ Delivered int64
+ DeliveredString string `xorm:"-"`
+
+ // History info.
+ IsSucceed bool
+ RequestContent string `xorm:"TEXT"`
+ RequestInfo *HookRequest `xorm:"-"`
+ ResponseContent string `xorm:"TEXT"`
+ ResponseInfo *HookResponse `xorm:"-"`
+}
+
+func (t *HookTask) BeforeUpdate() {
+ if t.RequestInfo != nil {
+ t.RequestContent = t.MarshalJSON(t.RequestInfo)
+ }
+ if t.ResponseInfo != nil {
+ t.ResponseContent = t.MarshalJSON(t.ResponseInfo)
+ }
+}
+
+func (t *HookTask) AfterSet(colName string, _ xorm.Cell) {
+ var err error
+ switch colName {
+ case "delivered":
+ t.DeliveredString = time.Unix(0, t.Delivered).Format("2006-01-02 15:04:05 MST")
+
+ case "request_content":
+ if len(t.RequestContent) == 0 {
+ return
+ }
+
+ t.RequestInfo = &HookRequest{}
+ if err = json.Unmarshal([]byte(t.RequestContent), t.RequestInfo); err != nil {
+ log.Error(3, "Unmarshal[%d]: %v", t.ID, err)
+ }
+
+ case "response_content":
+ if len(t.ResponseContent) == 0 {
+ return
+ }
+
+ t.ResponseInfo = &HookResponse{}
+ if err = json.Unmarshal([]byte(t.ResponseContent), t.ResponseInfo); err != nil {
+ log.Error(3, "Unmarshal[%d]: %v", t.ID, err)
+ }
+ }
+}
+
+func (t *HookTask) MarshalJSON(v interface{}) string {
+ p, err := json.Marshal(v)
+ if err != nil {
+ log.Error(3, "Marshal[%d]: %v", t.ID, err)
+ }
+ return string(p)
+}
+
+// HookTasks returns a list of hook tasks by given conditions.
+func HookTasks(hookID int64, page int) ([]*HookTask, error) {
+ tasks := make([]*HookTask, 0, setting.Webhook.PagingNum)
+ return tasks, x.Limit(setting.Webhook.PagingNum, (page-1)*setting.Webhook.PagingNum).Desc("id").Find(&tasks)
}
// CreateHookTask creates a new hook task,
@@ -303,7 +377,7 @@ func CreateHookTask(t *HookTask) error {
if err != nil {
return err
}
- t.Uuid = uuid.NewV4().String()
+ t.UUID = uuid.NewV4().String()
t.PayloadContent = string(data)
_, err = x.Insert(t)
return err
@@ -348,9 +422,11 @@ func (q *hookQueue) AddRepoID(id int64) {
var HookQueue *hookQueue
func deliverHook(t *HookTask) {
+ t.IsDelivered = true
+
timeout := time.Duration(setting.Webhook.DeliverTimeout) * time.Second
- req := httplib.Post(t.Url).SetTimeout(timeout, timeout).
- Header("X-Gogs-Delivery", t.Uuid).
+ req := httplib.Post(t.URL).SetTimeout(timeout, timeout).
+ Header("X-Gogs-Delivery", t.UUID).
Header("X-Gogs-Event", string(t.EventType)).
SetTLSClientConfig(&tls.Config{InsecureSkipVerify: setting.Webhook.SkipTLSVerify})
@@ -361,42 +437,68 @@ func deliverHook(t *HookTask) {
req.Param("payload", t.PayloadContent)
}
- t.IsDelivered = true
+ // Record delivery information.
+ t.RequestInfo = &HookRequest{
+ Headers: map[string]string{},
+ }
+ for k, vals := range req.Headers() {
+ t.RequestInfo.Headers[k] = strings.Join(vals, ",")
+ }
- // FIXME: record response.
- switch t.Type {
- case GOGS:
- {
- if resp, err := req.Response(); err != nil {
- log.Error(5, "Delivery: %v", err)
- } else {
- resp.Body.Close()
- t.IsSucceed = true
- }
+ t.ResponseInfo = &HookResponse{
+ Headers: map[string]string{},
+ }
+
+ defer func() {
+ t.Delivered = time.Now().UTC().UnixNano()
+ if t.IsSucceed {
+ log.Trace("Hook delivered: %s", t.UUID)
}
- case SLACK:
- {
- if resp, err := req.Response(); err != nil {
- log.Error(5, "Delivery: %v", err)
- } else {
- defer resp.Body.Close()
- contents, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- log.Error(5, "%s", err)
- } else {
- if string(contents) != "ok" {
- log.Error(5, "slack failed with: %s", string(contents))
- } else {
- t.IsSucceed = true
- }
- }
- }
+
+ // Update webhook last delivery status.
+ w, err := GetWebhookByID(t.HookID)
+ if err != nil {
+ log.Error(5, "GetWebhookByID: %v", err)
+ return
+ }
+ if t.IsSucceed {
+ w.LastStatus = HOOK_STATUS_SUCCEED
+ } else {
+ w.LastStatus = HOOK_STATUS_FAILED
}
+ if err = UpdateWebhook(w); err != nil {
+ log.Error(5, "UpdateWebhook: %v", err)
+ return
+ }
+ }()
+
+ resp, err := req.Response()
+ if err != nil {
+ t.ResponseInfo.Body = fmt.Sprintf("Delivery: %v", err)
+ return
}
+ defer resp.Body.Close()
- t.Delivered = time.Now().UTC().UnixNano()
- if t.IsSucceed {
- log.Trace("Hook delivered(%s): %s", t.Uuid, t.PayloadContent)
+ // Status code is 20x can be seen as succeed.
+ t.IsSucceed = resp.StatusCode/100 == 2
+ t.ResponseInfo.Status = resp.StatusCode
+ for k, vals := range resp.Header {
+ t.ResponseInfo.Headers[k] = strings.Join(vals, ",")
+ }
+
+ p, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ t.ResponseInfo.Body = fmt.Sprintf("read body: %s", err)
+ return
+ }
+ t.ResponseInfo.Body = string(p)
+
+ switch t.Type {
+ case SLACK:
+ if t.ResponseInfo.Body != "ok" {
+ log.Error(5, "slack failed with: %s", t.ResponseInfo.Body)
+ t.IsSucceed = false
+ }
}
}