Просмотр исходного кода

repo/webhook: able to retrigger delivery history (#2187)

Unknwon 8 лет назад
Родитель
Сommit
2807274e2d

+ 6 - 2
cmd/web.go

@@ -460,12 +460,16 @@ func runWeb(ctx *cli.Context) error {
 				m.Post("/gogs/new", bindIgnErr(form.NewWebhook{}), repo.WebHooksNewPost)
 				m.Post("/slack/new", bindIgnErr(form.NewSlackHook{}), repo.SlackHooksNewPost)
 				m.Post("/discord/new", bindIgnErr(form.NewDiscordHook{}), repo.DiscordHooksNewPost)
-				m.Get("/:id", repo.WebHooksEdit)
-				m.Post("/:id/test", repo.TestWebhook)
 				m.Post("/gogs/:id", bindIgnErr(form.NewWebhook{}), repo.WebHooksEditPost)
 				m.Post("/slack/:id", bindIgnErr(form.NewSlackHook{}), repo.SlackHooksEditPost)
 				m.Post("/discord/:id", bindIgnErr(form.NewDiscordHook{}), repo.DiscordHooksEditPost)
 
+				m.Group("/:id", func() {
+					m.Get("", repo.WebHooksEdit)
+					m.Post("/test", repo.TestWebhook)
+					m.Post("/redelivery", repo.RedeliveryWebhook)
+				})
+
 				m.Group("/git", func() {
 					m.Get("", repo.SettingsGitHooks)
 					m.Combo("/:name").Get(repo.SettingsGitHooksEdit).

+ 2 - 0
conf/locale/locale_en-US.ini

@@ -742,6 +742,8 @@ settings.webhook_deletion_success = Webhook has been deleted successfully!
 settings.webhook.test_delivery = Test Delivery
 settings.webhook.test_delivery_desc = Send a fake push event delivery to test your webhook settings
 settings.webhook.test_delivery_success = Test webhook has been added to delivery queue. It may take few seconds before it shows up in the delivery history.
+settings.webhook.redelivery = Redelivery
+settings.webhook.redelivery_success = Hook task '%s' has been readded to delivery queue. It may take few seconds to update delivery status in history.
 settings.webhook.request = Request
 settings.webhook.response = Response
 settings.webhook.headers = Headers

+ 1 - 1
gogs.go

@@ -16,7 +16,7 @@ import (
 	"github.com/gogits/gogs/modules/setting"
 )
 
-const APP_VER = "0.10.23.0318"
+const APP_VER = "0.10.24.0319"
 
 func init() {
 	setting.AppVer = APP_VER

+ 0 - 20
models/error.go

@@ -436,26 +436,6 @@ func (err ErrBranchNotExist) Error() string {
 	return fmt.Sprintf("branch does not exist [name: %s]", err.Name)
 }
 
-//  __      __      ___.   .__                   __
-// /  \    /  \ ____\_ |__ |  |__   ____   ____ |  | __
-// \   \/\/   // __ \| __ \|  |  \ /  _ \ /  _ \|  |/ /
-//  \        /\  ___/| \_\ \   Y  (  <_> |  <_> )    <
-//   \__/\  /  \___  >___  /___|  /\____/ \____/|__|_ \
-//        \/       \/    \/     \/                   \/
-
-type ErrWebhookNotExist struct {
-	ID int64
-}
-
-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)
-}
-
 // .___
 // |   | ______ ________ __   ____
 // |   |/  ___//  ___/  |  \_/ __ \

+ 34 - 0
models/errors/webhook.go

@@ -0,0 +1,34 @@
+// Copyright 2017 The Gogs 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 errors
+
+import "fmt"
+
+type WebhookNotExist struct {
+	ID int64
+}
+
+func IsWebhookNotExist(err error) bool {
+	_, ok := err.(WebhookNotExist)
+	return ok
+}
+
+func (err WebhookNotExist) Error() string {
+	return fmt.Sprintf("webhook does not exist [id: %d]", err.ID)
+}
+
+type HookTaskNotExist struct {
+	HookID int64
+	UUID   string
+}
+
+func IsHookTaskNotExist(err error) bool {
+	_, ok := err.(HookTaskNotExist)
+	return ok
+}
+
+func (err HookTaskNotExist) Error() string {
+	return fmt.Sprintf("hook task does not exist [hook_id: %d, uuid: %s]", err.HookID, err.UUID)
+}

+ 19 - 3
models/webhook.go

@@ -21,6 +21,7 @@ import (
 
 	api "github.com/gogits/go-gogs-client"
 
+	"github.com/gogits/gogs/models/errors"
 	"github.com/gogits/gogs/modules/httplib"
 	"github.com/gogits/gogs/modules/setting"
 	"github.com/gogits/gogs/modules/sync"
@@ -241,7 +242,7 @@ func getWebhook(bean *Webhook) (*Webhook, error) {
 	if err != nil {
 		return nil, err
 	} else if !has {
-		return nil, ErrWebhookNotExist{bean.ID}
+		return nil, errors.WebhookNotExist{bean.ID}
 	}
 	return bean, nil
 }
@@ -494,6 +495,21 @@ func createHookTask(e Engine, t *HookTask) error {
 	return err
 }
 
+// GetHookTaskOfWebhookByUUID returns hook task of given webhook by UUID.
+func GetHookTaskOfWebhookByUUID(webhookID int64, uuid string) (*HookTask, error) {
+	hookTask := &HookTask{
+		HookID: webhookID,
+		UUID:   uuid,
+	}
+	has, err := x.Get(hookTask)
+	if err != nil {
+		return nil, err
+	} else if !has {
+		return nil, errors.HookTaskNotExist{webhookID, uuid}
+	}
+	return hookTask, nil
+}
+
 // UpdateHookTask updates information of hook task.
 func UpdateHookTask(t *HookTask) error {
 	_, err := x.Id(t.ID).AllCols().Update(t)
@@ -704,7 +720,7 @@ func (t *HookTask) deliver() {
 // TODO: shoot more hooks at same time.
 func DeliverHooks() {
 	tasks := make([]*HookTask, 0, 10)
-	x.Where("is_delivered=?", false).Iterate(new(HookTask),
+	x.Where("is_delivered = ?", false).Iterate(new(HookTask),
 		func(idx int, bean interface{}) error {
 			t := bean.(*HookTask)
 			t.deliver()
@@ -725,7 +741,7 @@ func DeliverHooks() {
 		HookQueue.Remove(repoID)
 
 		tasks = make([]*HookTask, 0, 5)
-		if err := x.Where("repo_id=? AND is_delivered=?", repoID, false).Find(&tasks); err != nil {
+		if err := x.Where("repo_id = ?", repoID).And("is_delivered = ?", false).Find(&tasks); err != nil {
 			log.Error(4, "Get repository [%s] hook tasks: %v", repoID, err)
 			continue
 		}

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
modules/bindata/bindata.go


+ 9 - 0
public/css/gogs.css

@@ -2375,6 +2375,15 @@ footer .ui.language .menu {
   margin-left: 26px;
   padding-top: 0;
 }
+.webhook .hook.history.list .right.menu .redelivery.button {
+  font-size: 12px;
+  margin-top: 6px;
+  height: 30px;
+}
+.webhook .hook.history.list .right.menu .redelivery.button .octicon {
+  font: normal normal normal 13px/1 Octicons;
+  width: 12px;
+}
 .user-cards .list {
   padding: 0;
 }

+ 67 - 71
public/js/gogs.js

@@ -513,20 +513,6 @@ function initRepository() {
     }
 }
 
-function initRepositoryCollaboration() {
-    console.log('initRepositoryCollaboration');
-
-    // Change collaborator access mode
-    $('.access-mode.menu .item').click(function () {
-        var $menu = $(this).parent();
-        $.post($menu.data('url'), {
-            "_csrf": csrf,
-            "uid": $menu.data('uid'),
-            "mode": $(this).data('value')
-        })
-    });
-}
-
 function initWikiForm() {
     var $editArea = $('.repository.wiki textarea#edit_area');
     if ($editArea.length > 0) {
@@ -828,61 +814,6 @@ function initOrganization() {
     }
 }
 
-function initUserSettings() {
-    console.log('initUserSettings');
-
-    // Options
-    if ($('.user.settings.profile').length > 0) {
-        $('#username').keyup(function () {
-            var $prompt = $('#name-change-prompt');
-            if ($(this).val().toString().toLowerCase() != $(this).data('name').toString().toLowerCase()) {
-                $prompt.show();
-            } else {
-                $prompt.hide();
-            }
-        });
-    }
-}
-
-function initWebhook() {
-    if ($('.new.webhook').length == 0) {
-        return;
-    }
-
-    $('.events.checkbox input').change(function () {
-        if ($(this).is(':checked')) {
-            $('.events.fields').show();
-        }
-    });
-    $('.non-events.checkbox input').change(function () {
-        if ($(this).is(':checked')) {
-            $('.events.fields').hide();
-        }
-    });
-
-    // Highlight payload on first click
-    $('.hook.history.list .toggle.button').click(function () {
-        $($(this).data('target') + ' .nohighlight').each(function () {
-            var $this = $(this);
-            $this.removeClass('nohighlight');
-            setTimeout(function(){ hljs.highlightBlock($this[0]) }, 500);
-        })
-    })
-
-    // Test delivery
-    $('#test-delivery').click(function () {
-        var $this = $(this);
-        $this.addClass('loading disabled');
-        $.post($this.data('link'), {
-            "_csrf": csrf
-        }).done(
-            setTimeout(function () {
-                window.location.href = $this.data('redirect');
-            }, 5000)
-        )
-    });
-}
-
 function initAdmin() {
     if ($('.admin').length == 0) {
         return;
@@ -1152,6 +1083,71 @@ function initCodeView() {
     }
 }
 
+function initUserSettings() {
+    console.log('initUserSettings');
+
+    // Options
+    if ($('.user.settings.profile').length > 0) {
+        $('#username').keyup(function () {
+            var $prompt = $('#name-change-prompt');
+            if ($(this).val().toString().toLowerCase() != $(this).data('name').toString().toLowerCase()) {
+                $prompt.show();
+            } else {
+                $prompt.hide();
+            }
+        });
+    }
+}
+
+function initRepositoryCollaboration() {
+    console.log('initRepositoryCollaboration');
+
+    // Change collaborator access mode
+    $('.access-mode.menu .item').click(function () {
+        var $menu = $(this).parent();
+        $.post($menu.data('url'), {
+            "_csrf": csrf,
+            "uid": $menu.data('uid'),
+            "mode": $(this).data('value')
+        })
+    });
+}
+
+function initWebhookSettings() {
+    $('.events.checkbox input').change(function () {
+        if ($(this).is(':checked')) {
+            $('.events.fields').show();
+        }
+    });
+    $('.non-events.checkbox input').change(function () {
+        if ($(this).is(':checked')) {
+            $('.events.fields').hide();
+        }
+    });
+
+    // Highlight payload on first click
+    $('.hook.history.list .toggle.button').click(function () {
+        $($(this).data('target') + ' .nohighlight').each(function () {
+            var $this = $(this);
+            $this.removeClass('nohighlight');
+            setTimeout(function(){ hljs.highlightBlock($this[0]) }, 500);
+        })
+    })
+
+    // Trigger delivery
+    $('.delivery.button, .redelivery.button').click(function () {
+        var $this = $(this);
+        $this.addClass('loading disabled');
+        $.post($this.data('link'), {
+            "_csrf": csrf
+        }).done(
+            setTimeout(function () {
+                window.location.href = $this.data('redirect');
+            }, 5000)
+        );
+    });
+}
+
 $(document).ready(function () {
     csrf = $('meta[name=_csrf]').attr("content");
     suburl = $('meta[name=_suburl]').attr("content");
@@ -1359,7 +1355,6 @@ $(document).ready(function () {
     initEditForm();
     initEditor();
     initOrganization();
-    initWebhook();
     initAdmin();
     initCodeView();
 
@@ -1379,7 +1374,8 @@ $(document).ready(function () {
 
     var routes = {
         'div.user.settings': initUserSettings,
-        'div.repository.settings.collaboration': initRepositoryCollaboration
+        'div.repository.settings.collaboration': initRepositoryCollaboration,
+        'div.webhook.settings': initWebhookSettings
     };
 
     var selector;

+ 15 - 0
public/less/_repository.less

@@ -1411,6 +1411,21 @@
 }
 // End of .repository
 
+// Should apply organization webhooks page
+.webhook .hook.history.list {
+	.right.menu {
+		.redelivery.button {
+			font-size: 12px;
+			margin-top: 6px;
+			height: 30px;
+			.octicon {
+				font: normal normal normal 13px/1 Octicons;
+				width: 12px;
+			}
+		}
+	}
+}
+
 &.user-cards {
 	.list {
 		padding: 0;

+ 2 - 1
routers/api/v1/repo/hook.go

@@ -12,6 +12,7 @@ import (
 	api "github.com/gogits/go-gogs-client"
 
 	"github.com/gogits/gogs/models"
+	"github.com/gogits/gogs/models/errors"
 	"github.com/gogits/gogs/modules/context"
 	"github.com/gogits/gogs/routers/api/v1/convert"
 )
@@ -106,7 +107,7 @@ func CreateHook(ctx *context.APIContext, form api.CreateHookOption) {
 func EditHook(ctx *context.APIContext, form api.EditHookOption) {
 	w, err := models.GetWebhookOfRepoByID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id"))
 	if err != nil {
-		if models.IsErrWebhookNotExist(err) {
+		if errors.IsWebhookNotExist(err) {
 			ctx.Status(404)
 		} else {
 			ctx.Error(500, "GetWebhookOfRepoByID", err)

+ 31 - 14
routers/repo/webhook.go

@@ -55,6 +55,7 @@ type OrgRepoCtx struct {
 // getOrgRepoCtx determines whether this is a repo context or organization context.
 func getOrgRepoCtx(ctx *context.Context) (*OrgRepoCtx, error) {
 	if len(ctx.Repo.RepoLink) > 0 {
+		ctx.Data["PageIsRepositoryContext"] = true
 		return &OrgRepoCtx{
 			RepoID:      ctx.Repo.Repository.ID,
 			Link:        ctx.Repo.RepoLink,
@@ -63,6 +64,7 @@ func getOrgRepoCtx(ctx *context.Context) (*OrgRepoCtx, error) {
 	}
 
 	if len(ctx.Org.OrgLink) > 0 {
+		ctx.Data["PageIsOrganizationContext"] = true
 		return &OrgRepoCtx{
 			OrgID:       ctx.Org.Organization.ID,
 			Link:        ctx.Org.OrgLink,
@@ -284,11 +286,7 @@ func checkWebhook(ctx *context.Context) (*OrgRepoCtx, *models.Webhook) {
 		w, err = models.GetWebhookByOrgID(ctx.Org.Organization.ID, ctx.ParamsInt64(":id"))
 	}
 	if err != nil {
-		if models.IsErrWebhookNotExist(err) {
-			ctx.Handle(404, "GetWebhookByID", nil)
-		} else {
-			ctx.Handle(500, "GetWebhookByID", err)
-		}
+		ctx.NotFoundOrServerError("GetWebhookOfRepoByID/GetWebhookByOrgID", errors.IsWebhookNotExist, err)
 		return nil, nil
 	}
 
@@ -469,8 +467,7 @@ func TestWebhook(ctx *context.Context) {
 		if err == nil {
 			authorUsername = author.Name
 		} else if !errors.IsUserNotExist(err) {
-			ctx.Flash.Error(fmt.Sprintf("GetUserByEmail.(author) [%s]: %v", commit.Author.Email, err))
-			ctx.Status(500)
+			ctx.Handle(500, "GetUserByEmail.(author)", err)
 			return
 		}
 
@@ -478,16 +475,14 @@ func TestWebhook(ctx *context.Context) {
 		if err == nil {
 			committerUsername = committer.Name
 		} else if !errors.IsUserNotExist(err) {
-			ctx.Flash.Error(fmt.Sprintf("GetUserByEmail.(committer) [%s]: %v", commit.Committer.Email, err))
-			ctx.Status(500)
+			ctx.Handle(500, "GetUserByEmail.(committer)", err)
 			return
 		}
 	}
 
 	fileStatus, err := commit.FileStatus()
 	if err != nil {
-		ctx.Flash.Error("FileStatus: " + err.Error())
-		ctx.Status(500)
+		ctx.Handle(500, "FileStatus", err)
 		return
 	}
 
@@ -520,15 +515,37 @@ func TestWebhook(ctx *context.Context) {
 		Pusher: apiUser,
 		Sender: apiUser,
 	}
-	if err := models.TestWebhook(ctx.Repo.Repository, models.HOOK_EVENT_PUSH, p, ctx.QueryInt64("id")); err != nil {
-		ctx.Flash.Error("TestWebhook: " + err.Error())
-		ctx.Status(500)
+	if err := models.TestWebhook(ctx.Repo.Repository, models.HOOK_EVENT_PUSH, p, ctx.ParamsInt64("id")); err != nil {
+		ctx.Handle(500, "TestWebhook", err)
 	} else {
 		ctx.Flash.Info(ctx.Tr("repo.settings.webhook.test_delivery_success"))
 		ctx.Status(200)
 	}
 }
 
+func RedeliveryWebhook(ctx *context.Context) {
+	webhook, err := models.GetWebhookOfRepoByID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id"))
+	if err != nil {
+		ctx.NotFoundOrServerError("GetWebhookOfRepoByID/GetWebhookByOrgID", errors.IsWebhookNotExist, err)
+		return
+	}
+
+	hookTask, err := models.GetHookTaskOfWebhookByUUID(webhook.ID, ctx.Query("uuid"))
+	if err != nil {
+		ctx.NotFoundOrServerError("GetHookTaskOfWebhookByUUID/GetWebhookByOrgID", errors.IsHookTaskNotExist, err)
+		return
+	}
+
+	hookTask.IsDelivered = false
+	if err = models.UpdateHookTask(hookTask); err != nil {
+		ctx.Handle(500, "UpdateHookTask", err)
+	} else {
+		go models.HookQueue.Add(ctx.Repo.Repository.ID)
+		ctx.Flash.Info(ctx.Tr("repo.settings.webhook.redelivery_success", hookTask.UUID))
+		ctx.Status(200)
+	}
+}
+
 func DeleteWebhook(ctx *context.Context) {
 	if err := models.DeleteWebhookOfRepoByID(ctx.Repo.Repository.ID, ctx.QueryInt64("id")); err != nil {
 		ctx.Flash.Error("DeleteWebhookByRepoID: " + err.Error())

+ 1 - 1
templates/.VERSION

@@ -1 +1 @@
-0.10.23.0318
+0.10.24.0319

+ 2 - 2
templates/org/settings/webhook_new.tmpl

@@ -11,8 +11,8 @@
 					<div class="ui right">
 						{{if eq .HookType "gogs"}}
 							<img class="img-13" src="{{AppSubUrl}}/img/favicon.png">
-						{{else if eq .HookType "slack"}}
-							<img class="img-13" src="{{AppSubUrl}}/img/slack.png">
+						{{else}}
+							<img class="img-13" src="{{AppSubUrl}}/img/{{.HookType}}.png">
 						{{end}}
 					</div>
 				</h4>

+ 8 - 3
templates/repo/settings/webhook/history.tmpl

@@ -1,10 +1,10 @@
 {{if .PageIsSettingsHooksEdit}}
 	<h4 class="ui top attached header">
 		{{.i18n.Tr "repo.settings.recent_deliveries"}}
-		{{if .IsRepositoryAdmin}}
+		{{if .PageIsRepositoryContext}}
 			<div class="ui right">
-				<button class="ui teal tiny button poping up" id="test-delivery" data-content=
-				"{{.i18n.Tr "repo.settings.webhook.test_delivery_desc"}}" data-variation="inverted tiny" data-link="{{.Link}}/test?id={{.Webhook.ID}}" data-redirect="{{.Link}}">{{.i18n.Tr "repo.settings.webhook.test_delivery"}}</button>
+				<button class="ui teal tiny delivery button poping up" data-content=
+				"{{.i18n.Tr "repo.settings.webhook.test_delivery_desc"}}" data-variation="inverted tiny" data-link="{{.Link}}/test" data-redirect="{{.Link}}">{{.i18n.Tr "repo.settings.webhook.test_delivery"}}</button>
 			</div>
 		{{end}}
 	</h4>
@@ -40,6 +40,11 @@
 									<span class="ui label">N/A</span>
 								{{end}}
 							</a>
+							{{if $.PageIsRepositoryContext}}
+								<div class="right menu">
+									<div class="ui basic redelivery button" data-link="{{$.Link}}/redelivery?uuid={{.UUID}}" data-redirect="{{$.Link}}"><i class="octicon octicon-sync"></i> <span>{{$.i18n.Tr "repo.settings.webhook.redelivery"}}</span></div>
+								</div>
+							{{end}}
 						</div>
 						<div class="ui bottom attached tab segment active" data-tab="request-{{.ID}}">
 							{{if .RequestInfo}}

Некоторые файлы не были показаны из-за большого количества измененных файлов