aboutsummaryrefslogtreecommitdiffstats
path: root/models/webhook/hooktask.go
diff options
context:
space:
mode:
authorLunny Xiao <xiaolunwen@gmail.com>2021-11-10 13:13:16 +0800
committerGitHub <noreply@github.com>2021-11-10 13:13:16 +0800
commit33fca2b537d36cf998dd27425b2bb8ed5b0965f3 (patch)
tree817f392502e1c176a5cd7e80290520cb940a8416 /models/webhook/hooktask.go
parentedbaa5d3f05b5ca397524587ba9db15edd61bc29 (diff)
downloadgitea-33fca2b537d36cf998dd27425b2bb8ed5b0965f3.tar.gz
gitea-33fca2b537d36cf998dd27425b2bb8ed5b0965f3.zip
Move webhook into models/webhook/ (#17579)
Diffstat (limited to 'models/webhook/hooktask.go')
-rw-r--r--models/webhook/hooktask.go280
1 files changed, 280 insertions, 0 deletions
diff --git a/models/webhook/hooktask.go b/models/webhook/hooktask.go
new file mode 100644
index 0000000000..1967ded298
--- /dev/null
+++ b/models/webhook/hooktask.go
@@ -0,0 +1,280 @@
+// Copyright 2017 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package webhook
+
+import (
+ "context"
+ "time"
+
+ "code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/modules/json"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
+ api "code.gitea.io/gitea/modules/structs"
+
+ gouuid "github.com/google/uuid"
+)
+
+// ___ ___ __ ___________ __
+// / | \ ____ ____ | | _\__ ___/____ _____| | __
+// / ~ \/ _ \ / _ \| |/ / | | \__ \ / ___/ |/ /
+// \ Y ( <_> | <_> ) < | | / __ \_\___ \| <
+// \___|_ / \____/ \____/|__|_ \ |____| (____ /____ >__|_ \
+// \/ \/ \/ \/ \/
+
+// HookEventType is the type of an hook event
+type HookEventType string
+
+// Types of hook events
+const (
+ HookEventCreate HookEventType = "create"
+ HookEventDelete HookEventType = "delete"
+ HookEventFork HookEventType = "fork"
+ HookEventPush HookEventType = "push"
+ HookEventIssues HookEventType = "issues"
+ HookEventIssueAssign HookEventType = "issue_assign"
+ HookEventIssueLabel HookEventType = "issue_label"
+ HookEventIssueMilestone HookEventType = "issue_milestone"
+ HookEventIssueComment HookEventType = "issue_comment"
+ HookEventPullRequest HookEventType = "pull_request"
+ HookEventPullRequestAssign HookEventType = "pull_request_assign"
+ HookEventPullRequestLabel HookEventType = "pull_request_label"
+ HookEventPullRequestMilestone HookEventType = "pull_request_milestone"
+ HookEventPullRequestComment HookEventType = "pull_request_comment"
+ HookEventPullRequestReviewApproved HookEventType = "pull_request_review_approved"
+ HookEventPullRequestReviewRejected HookEventType = "pull_request_review_rejected"
+ HookEventPullRequestReviewComment HookEventType = "pull_request_review_comment"
+ HookEventPullRequestSync HookEventType = "pull_request_sync"
+ HookEventRepository HookEventType = "repository"
+ HookEventRelease HookEventType = "release"
+)
+
+// Event returns the HookEventType as an event string
+func (h HookEventType) Event() string {
+ switch h {
+ case HookEventCreate:
+ return "create"
+ case HookEventDelete:
+ return "delete"
+ case HookEventFork:
+ return "fork"
+ case HookEventPush:
+ return "push"
+ case HookEventIssues, HookEventIssueAssign, HookEventIssueLabel, HookEventIssueMilestone:
+ return "issues"
+ case HookEventPullRequest, HookEventPullRequestAssign, HookEventPullRequestLabel, HookEventPullRequestMilestone,
+ HookEventPullRequestSync:
+ return "pull_request"
+ case HookEventIssueComment, HookEventPullRequestComment:
+ return "issue_comment"
+ case HookEventPullRequestReviewApproved:
+ return "pull_request_approved"
+ case HookEventPullRequestReviewRejected:
+ return "pull_request_rejected"
+ case HookEventPullRequestReviewComment:
+ return "pull_request_comment"
+ case HookEventRepository:
+ return "repository"
+ case HookEventRelease:
+ return "release"
+ }
+ return ""
+}
+
+// HookRequest represents hook task request information.
+type HookRequest struct {
+ URL string `json:"url"`
+ HTTPMethod string `json:"http_method"`
+ 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
+ api.Payloader `xorm:"-"`
+ PayloadContent string `xorm:"TEXT"`
+ EventType HookEventType
+ 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 init() {
+ db.RegisterModel(new(HookTask))
+}
+
+// BeforeUpdate will be invoked by XORM before updating a record
+// representing this object
+func (t *HookTask) BeforeUpdate() {
+ if t.RequestInfo != nil {
+ t.RequestContent = t.simpleMarshalJSON(t.RequestInfo)
+ }
+ if t.ResponseInfo != nil {
+ t.ResponseContent = t.simpleMarshalJSON(t.ResponseInfo)
+ }
+}
+
+// AfterLoad updates the webhook object upon setting a column
+func (t *HookTask) AfterLoad() {
+ t.DeliveredString = time.Unix(0, t.Delivered).Format("2006-01-02 15:04:05 MST")
+
+ if len(t.RequestContent) == 0 {
+ return
+ }
+
+ t.RequestInfo = &HookRequest{}
+ if err := json.Unmarshal([]byte(t.RequestContent), t.RequestInfo); err != nil {
+ log.Error("Unmarshal RequestContent[%d]: %v", t.ID, err)
+ }
+
+ if len(t.ResponseContent) > 0 {
+ t.ResponseInfo = &HookResponse{}
+ if err := json.Unmarshal([]byte(t.ResponseContent), t.ResponseInfo); err != nil {
+ log.Error("Unmarshal ResponseContent[%d]: %v", t.ID, err)
+ }
+ }
+}
+
+func (t *HookTask) simpleMarshalJSON(v interface{}) string {
+ p, err := json.Marshal(v)
+ if err != nil {
+ log.Error("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, db.GetEngine(db.DefaultContext).
+ Limit(setting.Webhook.PagingNum, (page-1)*setting.Webhook.PagingNum).
+ Where("hook_id=?", hookID).
+ Desc("id").
+ Find(&tasks)
+}
+
+// CreateHookTask creates a new hook task,
+// it handles conversion from Payload to PayloadContent.
+func CreateHookTask(t *HookTask) error {
+ return createHookTask(db.GetEngine(db.DefaultContext), t)
+}
+
+func createHookTask(e db.Engine, t *HookTask) error {
+ data, err := t.Payloader.JSONPayload()
+ if err != nil {
+ return err
+ }
+ t.UUID = gouuid.New().String()
+ t.PayloadContent = string(data)
+ _, err = e.Insert(t)
+ return err
+}
+
+// UpdateHookTask updates information of hook task.
+func UpdateHookTask(t *HookTask) error {
+ _, err := db.GetEngine(db.DefaultContext).ID(t.ID).AllCols().Update(t)
+ return err
+}
+
+// FindUndeliveredHookTasks represents find the undelivered hook tasks
+func FindUndeliveredHookTasks() ([]*HookTask, error) {
+ tasks := make([]*HookTask, 0, 10)
+ if err := db.GetEngine(db.DefaultContext).Where("is_delivered=?", false).Find(&tasks); err != nil {
+ return nil, err
+ }
+ return tasks, nil
+}
+
+// FindRepoUndeliveredHookTasks represents find the undelivered hook tasks of one repository
+func FindRepoUndeliveredHookTasks(repoID int64) ([]*HookTask, error) {
+ tasks := make([]*HookTask, 0, 5)
+ if err := db.GetEngine(db.DefaultContext).Where("repo_id=? AND is_delivered=?", repoID, false).Find(&tasks); err != nil {
+ return nil, err
+ }
+ return tasks, nil
+}
+
+// CleanupHookTaskTable deletes rows from hook_task as needed.
+func CleanupHookTaskTable(ctx context.Context, cleanupType HookTaskCleanupType, olderThan time.Duration, numberToKeep int) error {
+ log.Trace("Doing: CleanupHookTaskTable")
+
+ if cleanupType == OlderThan {
+ deleteOlderThan := time.Now().Add(-olderThan).UnixNano()
+ deletes, err := db.GetEngine(db.DefaultContext).
+ Where("is_delivered = ? and delivered < ?", true, deleteOlderThan).
+ Delete(new(HookTask))
+ if err != nil {
+ return err
+ }
+ log.Trace("Deleted %d rows from hook_task", deletes)
+ } else if cleanupType == PerWebhook {
+ hookIDs := make([]int64, 0, 10)
+ err := db.GetEngine(db.DefaultContext).Table("webhook").
+ Where("id > 0").
+ Cols("id").
+ Find(&hookIDs)
+ if err != nil {
+ return err
+ }
+ for _, hookID := range hookIDs {
+ select {
+ case <-ctx.Done():
+ return db.ErrCancelledf("Before deleting hook_task records for hook id %d", hookID)
+ default:
+ }
+ if err = deleteDeliveredHookTasksByWebhook(hookID, numberToKeep); err != nil {
+ return err
+ }
+ }
+ }
+ log.Trace("Finished: CleanupHookTaskTable")
+ return nil
+}
+
+func deleteDeliveredHookTasksByWebhook(hookID int64, numberDeliveriesToKeep int) error {
+ log.Trace("Deleting hook_task rows for webhook %d, keeping the most recent %d deliveries", hookID, numberDeliveriesToKeep)
+ deliveryDates := make([]int64, 0, 10)
+ err := db.GetEngine(db.DefaultContext).Table("hook_task").
+ Where("hook_task.hook_id = ? AND hook_task.is_delivered = ? AND hook_task.delivered is not null", hookID, true).
+ Cols("hook_task.delivered").
+ Join("INNER", "webhook", "hook_task.hook_id = webhook.id").
+ OrderBy("hook_task.delivered desc").
+ Limit(1, int(numberDeliveriesToKeep)).
+ Find(&deliveryDates)
+ if err != nil {
+ return err
+ }
+
+ if len(deliveryDates) > 0 {
+ deletes, err := db.GetEngine(db.DefaultContext).
+ Where("hook_id = ? and is_delivered = ? and delivered <= ?", hookID, true, deliveryDates[0]).
+ Delete(new(HookTask))
+ if err != nil {
+ return err
+ }
+ log.Trace("Deleted %d hook_task rows for webhook %d", deletes, hookID)
+ } else {
+ log.Trace("No hook_task rows to delete for webhook %d", hookID)
+ }
+
+ return nil
+}