summaryrefslogtreecommitdiffstats
path: root/models
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
parentedbaa5d3f05b5ca397524587ba9db15edd61bc29 (diff)
downloadgitea-33fca2b537d36cf998dd27425b2bb8ed5b0965f3.tar.gz
gitea-33fca2b537d36cf998dd27425b2bb8ed5b0965f3.zip
Move webhook into models/webhook/ (#17579)
Diffstat (limited to 'models')
-rw-r--r--models/db/error.go29
-rw-r--r--models/error.go44
-rw-r--r--models/repo.go15
-rw-r--r--models/repo_avatar.go2
-rw-r--r--models/repo_generate.go7
-rw-r--r--models/statistic.go5
-rw-r--r--models/user.go2
-rw-r--r--models/webhook/hooktask.go280
-rw-r--r--models/webhook/main_test.go16
-rw-r--r--models/webhook/webhook.go (renamed from models/webhook.go)310
-rw-r--r--models/webhook/webhook_test.go (renamed from models/webhook_test.go)4
11 files changed, 376 insertions, 338 deletions
diff --git a/models/db/error.go b/models/db/error.go
new file mode 100644
index 0000000000..675247ed87
--- /dev/null
+++ b/models/db/error.go
@@ -0,0 +1,29 @@
+// Copyright 2021 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 db
+
+import "fmt"
+
+// ErrCancelled represents an error due to context cancellation
+type ErrCancelled struct {
+ Message string
+}
+
+// IsErrCancelled checks if an error is a ErrCancelled.
+func IsErrCancelled(err error) bool {
+ _, ok := err.(ErrCancelled)
+ return ok
+}
+
+func (err ErrCancelled) Error() string {
+ return "Cancelled: " + err.Message
+}
+
+// ErrCancelledf returns an ErrCancelled for the provided format and args
+func ErrCancelledf(format string, args ...interface{}) error {
+ return ErrCancelled{
+ fmt.Sprintf(format, args...),
+ }
+}
diff --git a/models/error.go b/models/error.go
index 1179fa6eb7..b365a67b73 100644
--- a/models/error.go
+++ b/models/error.go
@@ -84,28 +84,6 @@ func (err ErrSSHDisabled) Error() string {
return "SSH is disabled"
}
-// ErrCancelled represents an error due to context cancellation
-type ErrCancelled struct {
- Message string
-}
-
-// IsErrCancelled checks if an error is a ErrCancelled.
-func IsErrCancelled(err error) bool {
- _, ok := err.(ErrCancelled)
- return ok
-}
-
-func (err ErrCancelled) Error() string {
- return "Cancelled: " + err.Message
-}
-
-// ErrCancelledf returns an ErrCancelled for the provided format and args
-func ErrCancelledf(format string, args ...interface{}) error {
- return ErrCancelled{
- fmt.Sprintf(format, args...),
- }
-}
-
// ____ ___
// | | \______ ___________
// | | / ___// __ \_ __ \
@@ -1309,28 +1287,6 @@ func (err ErrSHAOrCommitIDNotProvided) Error() string {
return "a SHA or commit ID must be proved when updating a file"
}
-// __ __ ___. .__ __
-// / \ / \ ____\_ |__ | |__ ____ ____ | | __
-// \ \/\/ // __ \| __ \| | \ / _ \ / _ \| |/ /
-// \ /\ ___/| \_\ \ Y ( <_> | <_> ) <
-// \__/\ / \___ >___ /___| /\____/ \____/|__|_ \
-// \/ \/ \/ \/ \/
-
-// ErrWebhookNotExist represents a "WebhookNotExist" kind of error.
-type ErrWebhookNotExist struct {
- ID int64
-}
-
-// IsErrWebhookNotExist checks if an error is a ErrWebhookNotExist.
-func IsErrWebhookNotExist(err error) bool {
- _, ok := err.(ErrWebhookNotExist)
- return ok
-}
-
-func (err ErrWebhookNotExist) Error() string {
- return fmt.Sprintf("webhook does not exist [id: %d]", err.ID)
-}
-
// .___
// | | ______ ________ __ ____
// | |/ ___// ___/ | \_/ __ \
diff --git a/models/repo.go b/models/repo.go
index 10dc8d9b09..f44fc763a5 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -24,6 +24,7 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unit"
+ "code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup"
@@ -1153,7 +1154,7 @@ func CreateRepository(ctx context.Context, doer, u *User, repo *Repository, over
}
}
- if err = copyDefaultWebhooksToRepo(db.GetEngine(ctx), repo.ID); err != nil {
+ if err = webhook.CopyDefaultWebhooksToRepo(ctx, repo.ID); err != nil {
return fmt.Errorf("copyDefaultWebhooksToRepo: %v", err)
}
@@ -1509,7 +1510,7 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
&Comment{RefRepoID: repoID},
&CommitStatus{RepoID: repoID},
&DeletedBranch{RepoID: repoID},
- &HookTask{RepoID: repoID},
+ &webhook.HookTask{RepoID: repoID},
&LFSLock{RepoID: repoID},
&LanguageStat{RepoID: repoID},
&Milestone{RepoID: repoID},
@@ -1526,7 +1527,7 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
&Star{RepoID: repoID},
&Task{RepoID: repoID},
&Watch{RepoID: repoID},
- &Webhook{RepoID: repoID},
+ &webhook.Webhook{RepoID: repoID},
); err != nil {
return fmt.Errorf("deleteBeans: %v", err)
}
@@ -1932,7 +1933,7 @@ func CheckRepoStats(ctx context.Context) error {
select {
case <-ctx.Done():
log.Warn("CheckRepoStats: Cancelled before %s", checker.desc)
- return ErrCancelledf("before checking %s", checker.desc)
+ return db.ErrCancelledf("before checking %s", checker.desc)
default:
repoStatsCheck(ctx, checker)
}
@@ -1949,7 +1950,7 @@ func CheckRepoStats(ctx context.Context) error {
select {
case <-ctx.Done():
log.Warn("CheckRepoStats: Cancelled during %s for repo ID %d", desc, id)
- return ErrCancelledf("during %s for repo ID %d", desc, id)
+ return db.ErrCancelledf("during %s for repo ID %d", desc, id)
default:
}
log.Trace("Updating %s: %d", desc, id)
@@ -1972,7 +1973,7 @@ func CheckRepoStats(ctx context.Context) error {
select {
case <-ctx.Done():
log.Warn("CheckRepoStats: Cancelled")
- return ErrCancelledf("during %s for repo ID %d", desc, id)
+ return db.ErrCancelledf("during %s for repo ID %d", desc, id)
default:
}
log.Trace("Updating %s: %d", desc, id)
@@ -1995,7 +1996,7 @@ func CheckRepoStats(ctx context.Context) error {
select {
case <-ctx.Done():
log.Warn("CheckRepoStats: Cancelled")
- return ErrCancelledf("during %s for repo ID %d", desc, id)
+ return db.ErrCancelledf("during %s for repo ID %d", desc, id)
default:
}
log.Trace("Updating repository count 'num_forks': %d", id)
diff --git a/models/repo_avatar.go b/models/repo_avatar.go
index bb5f083dd5..6c5e03c0d0 100644
--- a/models/repo_avatar.go
+++ b/models/repo_avatar.go
@@ -64,7 +64,7 @@ func RemoveRandomAvatars(ctx context.Context) error {
repository := bean.(*Repository)
select {
case <-ctx.Done():
- return ErrCancelledf("before random avatars removed for %s", repository.FullName())
+ return db.ErrCancelledf("before random avatars removed for %s", repository.FullName())
default:
}
stringifiedID := strconv.FormatInt(repository.ID, 10)
diff --git a/models/repo_generate.go b/models/repo_generate.go
index 650da711a3..cef5fa7928 100644
--- a/models/repo_generate.go
+++ b/models/repo_generate.go
@@ -12,6 +12,7 @@ import (
"strings"
"code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/storage"
@@ -113,13 +114,13 @@ func GenerateGitHooks(ctx context.Context, templateRepo, generateRepo *Repositor
// GenerateWebhooks generates webhooks from a template repository
func GenerateWebhooks(ctx context.Context, templateRepo, generateRepo *Repository) error {
- templateWebhooks, err := ListWebhooksByOpts(&ListWebhookOptions{RepoID: templateRepo.ID})
+ templateWebhooks, err := webhook.ListWebhooksByOpts(&webhook.ListWebhookOptions{RepoID: templateRepo.ID})
if err != nil {
return err
}
for _, templateWebhook := range templateWebhooks {
- generateWebhook := &Webhook{
+ generateWebhook := &webhook.Webhook{
RepoID: generateRepo.ID,
URL: templateWebhook.URL,
HTTPMethod: templateWebhook.HTTPMethod,
@@ -132,7 +133,7 @@ func GenerateWebhooks(ctx context.Context, templateRepo, generateRepo *Repositor
Events: templateWebhook.Events,
Meta: templateWebhook.Meta,
}
- if err := createWebhook(db.GetEngine(ctx), generateWebhook); err != nil {
+ if err := webhook.CreateWebhook(ctx, generateWebhook); err != nil {
return err
}
}
diff --git a/models/statistic.go b/models/statistic.go
index 5e72dc713d..fab35e62dc 100644
--- a/models/statistic.go
+++ b/models/statistic.go
@@ -7,6 +7,7 @@ package models
import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/login"
+ "code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/modules/setting"
)
@@ -95,10 +96,10 @@ func GetStatistic() (stats Statistic) {
stats.Counter.Mirror, _ = e.Count(new(Mirror))
stats.Counter.Release, _ = e.Count(new(Release))
stats.Counter.LoginSource = login.CountSources()
- stats.Counter.Webhook, _ = e.Count(new(Webhook))
+ stats.Counter.Webhook, _ = e.Count(new(webhook.Webhook))
stats.Counter.Milestone, _ = e.Count(new(Milestone))
stats.Counter.Label, _ = e.Count(new(Label))
- stats.Counter.HookTask, _ = e.Count(new(HookTask))
+ stats.Counter.HookTask, _ = e.Count(new(webhook.HookTask))
stats.Counter.Team, _ = e.Count(new(Team))
stats.Counter.Attachment, _ = e.Count(new(Attachment))
stats.Counter.Project, _ = e.Count(new(Project))
diff --git a/models/user.go b/models/user.go
index d27c581bab..13347a46b7 100644
--- a/models/user.go
+++ b/models/user.go
@@ -1356,7 +1356,7 @@ func DeleteInactiveUsers(ctx context.Context, olderThan time.Duration) (err erro
for _, u := range users {
select {
case <-ctx.Done():
- return ErrCancelledf("Before delete inactive user %s", u.Name)
+ return db.ErrCancelledf("Before delete inactive user %s", u.Name)
default:
}
if err = DeleteUser(u); err != nil {
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
+}
diff --git a/models/webhook/main_test.go b/models/webhook/main_test.go
new file mode 100644
index 0000000000..f94612a755
--- /dev/null
+++ b/models/webhook/main_test.go
@@ -0,0 +1,16 @@
+// Copyright 2020 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 (
+ "path/filepath"
+ "testing"
+
+ "code.gitea.io/gitea/models/db"
+)
+
+func TestMain(m *testing.M) {
+ db.MainTest(m, filepath.Join("..", ".."), "webhook.yml", "hook_task.yml")
+}
diff --git a/models/webhook.go b/models/webhook/webhook.go
index 9d04f8f5e4..de8bd5e338 100644
--- a/models/webhook.go
+++ b/models/webhook/webhook.go
@@ -3,26 +3,44 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package models
+package webhook
import (
"context"
"fmt"
"strings"
- "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"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
- gouuid "github.com/google/uuid"
"xorm.io/builder"
)
+// __ __ ___. .__ __
+// / \ / \ ____\_ |__ | |__ ____ ____ | | __
+// \ \/\/ // __ \| __ \| | \ / _ \ / _ \| |/ /
+// \ /\ ___/| \_\ \ Y ( <_> | <_> ) <
+// \__/\ / \___ >___ /___| /\____/ \____/|__|_ \
+// \/ \/ \/ \/ \/
+
+// ErrWebhookNotExist represents a "WebhookNotExist" kind of error.
+type ErrWebhookNotExist struct {
+ ID int64
+}
+
+// IsErrWebhookNotExist checks if an error is a ErrWebhookNotExist.
+func IsErrWebhookNotExist(err error) bool {
+ _, ok := err.(ErrWebhookNotExist)
+ return ok
+}
+
+func (err ErrWebhookNotExist) Error() string {
+ return fmt.Sprintf("webhook does not exist [id: %d]", err.ID)
+}
+
// HookContentType is the content type of a web hook
type HookContentType int
@@ -162,7 +180,6 @@ type Webhook struct {
func init() {
db.RegisterModel(new(Webhook))
- db.RegisterModel(new(HookTask))
}
// AfterLoad updates the webhook object upon setting a column
@@ -350,14 +367,9 @@ func (w *Webhook) EventsArray() []string {
}
// CreateWebhook creates a new web hook.
-func CreateWebhook(w *Webhook) error {
- return createWebhook(db.GetEngine(db.DefaultContext), w)
-}
-
-func createWebhook(e db.Engine, w *Webhook) error {
+func CreateWebhook(ctx context.Context, w *Webhook) error {
w.Type = strings.TrimSpace(w.Type)
- _, err := e.Insert(w)
- return err
+ return db.Insert(ctx, w)
}
// getWebhook uses argument bean as query condition,
@@ -444,12 +456,12 @@ func CountWebhooksByOpts(opts *ListWebhookOptions) (int64, error) {
// GetDefaultWebhooks returns all admin-default webhooks.
func GetDefaultWebhooks() ([]*Webhook, error) {
- return getDefaultWebhooks(db.GetEngine(db.DefaultContext))
+ return getDefaultWebhooks(db.DefaultContext)
}
-func getDefaultWebhooks(e db.Engine) ([]*Webhook, error) {
+func getDefaultWebhooks(ctx context.Context) ([]*Webhook, error) {
webhooks := make([]*Webhook, 0, 5)
- return webhooks, e.
+ return webhooks, db.GetEngine(ctx).
Where("repo_id=? AND org_id=? AND is_system_webhook=?", 0, 0, false).
Find(&webhooks)
}
@@ -552,9 +564,9 @@ func DeleteDefaultSystemWebhook(id int64) error {
return sess.Commit()
}
-// copyDefaultWebhooksToRepo creates copies of the default webhooks in a new repo
-func copyDefaultWebhooksToRepo(e db.Engine, repoID int64) error {
- ws, err := getDefaultWebhooks(e)
+// CopyDefaultWebhooksToRepo creates copies of the default webhooks in a new repo
+func CopyDefaultWebhooksToRepo(ctx context.Context, repoID int64) error {
+ ws, err := getDefaultWebhooks(ctx)
if err != nil {
return fmt.Errorf("GetDefaultWebhooks: %v", err)
}
@@ -562,267 +574,9 @@ func copyDefaultWebhooksToRepo(e db.Engine, repoID int64) error {
for _, w := range ws {
w.ID = 0
w.RepoID = repoID
- if err := createWebhook(e, w); err != nil {
+ if err := CreateWebhook(ctx, w); err != nil {
return fmt.Errorf("CreateWebhook: %v", err)
}
}
return nil
}
-
-// ___ ___ __ ___________ __
-// / | \ ____ ____ | | _\__ ___/____ _____| | __
-// / ~ \/ _ \ / _ \| |/ / | | \__ \ / ___/ |/ /
-// \ 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:"-"`
-}
-
-// 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 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
-}
diff --git a/models/webhook_test.go b/models/webhook/webhook_test.go
index d48fa365be..df2c37b355 100644
--- a/models/webhook_test.go
+++ b/models/webhook/webhook_test.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package models
+package webhook
import (
"context"
@@ -92,7 +92,7 @@ func TestCreateWebhook(t *testing.T) {
Events: `{"push_only":false,"send_everything":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":true}}`,
}
db.AssertNotExistsBean(t, hook)
- assert.NoError(t, CreateWebhook(hook))
+ assert.NoError(t, CreateWebhook(db.DefaultContext, hook))
db.AssertExistsAndLoadBean(t, hook)
}