Browse Source

webhook APIs

Unknwon 9 years ago
parent
commit
55ddf225bb

+ 1 - 1
.gopmfile

@@ -13,7 +13,7 @@ github.com/go-sql-driver/mysql = commit:a197e5d405
 github.com/go-xorm/core = commit:4813c0110d
 github.com/go-xorm/xorm = commit:97e7703766
 github.com/gogits/chardet = commit:2404f77725
-github.com/gogits/go-gogs-client = commit:92e76d616a
+github.com/gogits/go-gogs-client = 
 github.com/lib/pq = commit:0dad96c0b9
 github.com/macaron-contrib/binding = commit:de6ed78668
 github.com/macaron-contrib/cache = commit:cd824f6f2d

+ 4 - 1
conf/locale/locale_en-US.ini

@@ -529,7 +529,10 @@ settings.add_webhook_desc = Gogs will send a <code>POST</code> request to the UR
 settings.payload_url = Payload URL
 settings.content_type = Content Type
 settings.secret = Secret
-settings.event_desc = Upon which events should this webhook be triggered?
+settings.slack_username = Username
+settings.slack_icon_url = Icon URL
+settings.slack_color = Color
+settings.event_desc = When should this webhook be triggered?
 settings.event_push_only = Just the <code>push</code> event.
 settings.event_send_everything = I need <strong>everything</strong>.
 settings.event_choose = Let me choose what I need.

+ 12 - 1
conf/locale/locale_zh-CN.ini

@@ -325,7 +325,9 @@ repo_lang=仓库语言
 repo_lang_helper=请选择 .gitignore 文件
 license=授权许可
 license_helper=请选择授权许可文件
-init_readme=使用 README.md 文件初始化仓库
+readme=自述文档
+readme_helper=请选择自述文档模板
+auto_init=使用选定的文件和模板初始化仓库
 create_repo=创建仓库
 default_branch=默认分支
 mirror_interval=镜像同步周期(小时)
@@ -527,8 +529,17 @@ settings.add_webhook_desc=我们会通过 <code>POST</code> 请求将订阅事
 settings.payload_url=推送地址
 settings.content_type=数据格式
 settings.secret=密钥文本
+settings.slack_username=服务名称
+settings.slack_icon_url=图标 URL
+settings.slack_color=颜色代码
 settings.event_desc=请设置您希望触发 Web 钩子的事件:
 settings.event_push_only=只推送 <code>push</code> 事件。
+settings.event_send_everything=请把 <strong>一切</strong> 都给我
+settings.event_choose=我的命运自己主宰
+settings.event_create=创建
+settings.event_create_desc=创建分支或标签
+settings.event_push=推送
+settings.event_push_desc=Git 仓库推送
 settings.active=是否激活
 settings.active_helper=当指定事件发生时我们将会触发此 Web 钩子。
 settings.add_hook_success=Web 钩子添加成功!

+ 21 - 7
models/webhook.go

@@ -97,11 +97,16 @@ type Webhook struct {
 	Updated      time.Time  `xorm:"UPDATED"`
 }
 
-// GetEvent handles conversion from Events to HookEvent.
-func (w *Webhook) GetEvent() {
-	w.HookEvent = &HookEvent{}
-	if err := json.Unmarshal([]byte(w.Events), w.HookEvent); err != nil {
-		log.Error(4, "webhook.GetEvent(%d): %v", w.ID, err)
+func (w *Webhook) AfterSet(colName string, _ xorm.Cell) {
+	var err error
+	switch colName {
+	case "events":
+		w.HookEvent = &HookEvent{}
+		if err = json.Unmarshal([]byte(w.Events), w.HookEvent); err != nil {
+			log.Error(3, "Unmarshal[%d]: %v", w.ID, err)
+		}
+	case "created":
+		w.Created = regulateTimeZone(w.Created)
 	}
 }
 
@@ -137,6 +142,17 @@ func (w *Webhook) HasPushEvent() bool {
 		(w.ChooseEvents && w.HookEvents.Push)
 }
 
+func (w *Webhook) EventsArray() []string {
+	events := make([]string, 0, 2)
+	if w.HasCreateEvent() {
+		events = append(events, "create")
+	}
+	if w.HasPushEvent() {
+		events = append(events, "push")
+	}
+	return events
+}
+
 // CreateWebhook creates a new web hook.
 func CreateWebhook(w *Webhook) error {
 	_, err := x.Insert(w)
@@ -382,8 +398,6 @@ func PrepareWebhooks(repo *Repository, event HookEventType, p api.Payloader) err
 	}
 
 	for _, w := range ws {
-		w.GetEvent()
-
 		switch event {
 		case HOOK_EVENT_CREATE:
 			if !w.HasCreateEvent() {

+ 10 - 12
models/webhook_slack.go

@@ -13,22 +13,20 @@ import (
 	api "github.com/gogits/go-gogs-client"
 
 	"github.com/gogits/gogs/modules/git"
-	"github.com/gogits/gogs/modules/setting"
-)
-
-const (
-	SLACK_COLOR string = "#dd4b39"
 )
 
 type SlackMeta struct {
-	Channel string `json:"channel"`
+	Channel  string `json:"channel"`
+	Username string `json:"username"`
+	IconURL  string `json:"icon_url"`
+	Color    string `json:"color"`
 }
 
 type SlackPayload struct {
 	Channel     string            `json:"channel"`
 	Text        string            `json:"text"`
 	Username    string            `json:"username"`
-	IconUrl     string            `json:"icon_url"`
+	IconURL     string            `json:"icon_url"`
 	UnfurlLinks int               `json:"unfurl_links"`
 	LinkNames   int               `json:"link_names"`
 	Attachments []SlackAttachment `json:"attachments"`
@@ -75,8 +73,8 @@ func getSlackCreatePayload(p *api.CreatePayload, slack *SlackMeta) (*SlackPayloa
 	return &SlackPayload{
 		Channel:  slack.Channel,
 		Text:     text,
-		Username: setting.AppName,
-		IconUrl:  setting.AppUrl + "/img/favicon.png",
+		Username: slack.Username,
+		IconURL:  slack.IconURL,
 	}, nil
 }
 
@@ -113,13 +111,13 @@ func getSlackPushPayload(p *api.PushPayload, slack *SlackMeta) (*SlackPayload, e
 		}
 	}
 
-	slackAttachments := []SlackAttachment{{Color: SLACK_COLOR, Text: attachmentText}}
+	slackAttachments := []SlackAttachment{{Color: slack.Color, Text: attachmentText}}
 
 	return &SlackPayload{
 		Channel:     slack.Channel,
 		Text:        text,
-		Username:    setting.AppName,
-		IconUrl:     setting.AppUrl + "/img/favicon.png",
+		Username:    slack.Username,
+		IconURL:     slack.IconURL,
 		Attachments: slackAttachments,
 	}, nil
 }

+ 3 - 0
modules/auth/repo_form.go

@@ -99,6 +99,9 @@ func (f *NewWebhookForm) Validate(ctx *macaron.Context, errs binding.Errors) bin
 type NewSlackHookForm struct {
 	PayloadURL string `binding:"Required`
 	Channel    string `binding:"Required"`
+	Username   string
+	IconURL    string
+	Color      string
 	WebhookForm
 }
 

+ 1 - 1
modules/base/base.go

@@ -4,7 +4,7 @@
 
 package base
 
-const DOC_URL = "http://gogs.io/docs"
+const DOC_URL = "https://github.com/gogits/go-gogs-client/wiki"
 
 type (
 	TplName string

File diff suppressed because it is too large
+ 0 - 0
modules/bindata/bindata.go


+ 59 - 46
routers/api/v1/repo_hooks.go

@@ -6,6 +6,9 @@ package v1
 
 import (
 	"encoding/json"
+	"fmt"
+
+	"github.com/Unknwon/com"
 
 	api "github.com/gogits/go-gogs-client"
 
@@ -14,8 +17,33 @@ import (
 	"github.com/gogits/gogs/modules/middleware"
 )
 
-// GET /repos/:username/:reponame/hooks
-// https://developer.github.com/v3/repos/hooks/#list-hooks
+// ToApiHook converts webhook to API format.
+func ToApiHook(repoLink string, w *models.Webhook) *api.Hook {
+	config := map[string]string{
+		"url":          w.URL,
+		"content_type": w.ContentType.Name(),
+	}
+	if w.HookTaskType == models.SLACK {
+		s := w.GetSlackHook()
+		config["channel"] = s.Channel
+		config["username"] = s.Username
+		config["icon_url"] = s.IconURL
+		config["color"] = s.Color
+	}
+
+	return &api.Hook{
+		ID:      w.ID,
+		Type:    w.HookTaskType.Name(),
+		URL:     fmt.Sprintf("%s/settings/hooks/%d", repoLink, w.ID),
+		Active:  w.IsActive,
+		Config:  config,
+		Events:  w.EventsArray(),
+		Updated: w.Updated,
+		Created: w.Created,
+	}
+}
+
+// https://github.com/gogits/go-gogs-client/wiki/Repositories#list-hooks
 func ListRepoHooks(ctx *middleware.Context) {
 	hooks, err := models.GetWebhooksByRepoId(ctx.Repo.Repository.ID)
 	if err != nil {
@@ -25,31 +53,13 @@ func ListRepoHooks(ctx *middleware.Context) {
 
 	apiHooks := make([]*api.Hook, len(hooks))
 	for i := range hooks {
-		h := &api.Hook{
-			ID:     hooks[i].ID,
-			Type:   hooks[i].HookTaskType.Name(),
-			Active: hooks[i].IsActive,
-			Config: make(map[string]string),
-		}
-
-		// Currently, onle have push event.
-		h.Events = []string{"push"}
-
-		h.Config["url"] = hooks[i].URL
-		h.Config["content_type"] = hooks[i].ContentType.Name()
-		if hooks[i].HookTaskType == models.SLACK {
-			s := hooks[i].GetSlackHook()
-			h.Config["channel"] = s.Channel
-		}
-
-		apiHooks[i] = h
+		apiHooks[i] = ToApiHook(ctx.Repo.RepoLink, hooks[i])
 	}
 
 	ctx.JSON(200, &apiHooks)
 }
 
-// POST /repos/:username/:reponame/hooks
-// https://developer.github.com/v3/repos/hooks/#create-a-hook
+// https://github.com/gogits/go-gogs-client/wiki/Repositories#create-a-hook
 func CreateRepoHook(ctx *middleware.Context, form api.CreateHookOption) {
 	if !models.IsValidHookTaskType(form.Type) {
 		ctx.JSON(422, &base.ApiJsonErr{"invalid hook type", base.DOC_URL})
@@ -72,7 +82,11 @@ func CreateRepoHook(ctx *middleware.Context, form api.CreateHookOption) {
 		ContentType: models.ToHookContentType(form.Config["content_type"]),
 		Secret:      form.Config["secret"],
 		HookEvent: &models.HookEvent{
-			PushOnly: true, // Only support it now.
+			ChooseEvents: true,
+			HookEvents: models.HookEvents{
+				Create: com.IsSliceContainsStr(form.Events, string(models.HOOK_EVENT_CREATE)),
+				Push:   com.IsSliceContainsStr(form.Events, string(models.HOOK_EVENT_PUSH)),
+			},
 		},
 		IsActive:     form.Active,
 		HookTaskType: models.ToHookTaskType(form.Type),
@@ -84,7 +98,10 @@ func CreateRepoHook(ctx *middleware.Context, form api.CreateHookOption) {
 			return
 		}
 		meta, err := json.Marshal(&models.SlackMeta{
-			Channel: channel,
+			Channel:  channel,
+			Username: form.Config["username"],
+			IconURL:  form.Config["icon_url"],
+			Color:    form.Config["color"],
 		})
 		if err != nil {
 			ctx.JSON(500, &base.ApiJsonErr{"slack: JSON marshal failed: " + err.Error(), base.DOC_URL})
@@ -101,25 +118,10 @@ func CreateRepoHook(ctx *middleware.Context, form api.CreateHookOption) {
 		return
 	}
 
-	apiHook := &api.Hook{
-		ID:     w.ID,
-		Type:   w.HookTaskType.Name(),
-		Events: []string{"push"},
-		Active: w.IsActive,
-		Config: map[string]string{
-			"url":          w.URL,
-			"content_type": w.ContentType.Name(),
-		},
-	}
-	if w.HookTaskType == models.SLACK {
-		s := w.GetSlackHook()
-		apiHook.Config["channel"] = s.Channel
-	}
-	ctx.JSON(201, apiHook)
+	ctx.JSON(201, ToApiHook(ctx.Repo.RepoLink, w))
 }
 
-// PATCH /repos/:username/:reponame/hooks/:id
-// https://developer.github.com/v3/repos/hooks/#edit-a-hook
+// https://github.com/gogits/go-gogs-client/wiki/Repositories#edit-a-hook
 func EditRepoHook(ctx *middleware.Context, form api.EditHookOption) {
 	w, err := models.GetWebhookByID(ctx.ParamsInt64(":id"))
 	if err != nil {
@@ -142,7 +144,10 @@ func EditRepoHook(ctx *middleware.Context, form api.EditHookOption) {
 		if w.HookTaskType == models.SLACK {
 			if channel, ok := form.Config["channel"]; ok {
 				meta, err := json.Marshal(&models.SlackMeta{
-					Channel: channel,
+					Channel:  channel,
+					Username: form.Config["username"],
+					IconURL:  form.Config["icon_url"],
+					Color:    form.Config["color"],
 				})
 				if err != nil {
 					ctx.JSON(500, &base.ApiJsonErr{"slack: JSON marshal failed: " + err.Error(), base.DOC_URL})
@@ -153,17 +158,25 @@ func EditRepoHook(ctx *middleware.Context, form api.EditHookOption) {
 		}
 	}
 
+	// Update events
+	w.PushOnly = false
+	w.SendEverything = false
+	w.ChooseEvents = true
+	w.Create = com.IsSliceContainsStr(form.Events, string(models.HOOK_EVENT_CREATE))
+	w.Push = com.IsSliceContainsStr(form.Events, string(models.HOOK_EVENT_PUSH))
+	if err = w.UpdateEvent(); err != nil {
+		ctx.JSON(500, &base.ApiJsonErr{"UpdateEvent: " + err.Error(), base.DOC_URL})
+		return
+	}
+
 	if form.Active != nil {
 		w.IsActive = *form.Active
 	}
 
-	// FIXME: edit events
 	if err := models.UpdateWebhook(w); err != nil {
 		ctx.JSON(500, &base.ApiJsonErr{"UpdateWebhook: " + err.Error(), base.DOC_URL})
 		return
 	}
 
-	ctx.JSON(200, map[string]interface{}{
-		"ok": true,
-	})
+	ctx.JSON(200, ToApiHook(ctx.Repo.RepoLink, w))
 }

+ 8 - 3
routers/repo/setting.go

@@ -396,7 +396,10 @@ func SlackHooksNewPost(ctx *middleware.Context, form auth.NewSlackHookForm) {
 	}
 
 	meta, err := json.Marshal(&models.SlackMeta{
-		Channel: form.Channel,
+		Channel:  form.Channel,
+		Username: form.Username,
+		IconURL:  form.IconURL,
+		Color:    form.Color,
 	})
 	if err != nil {
 		ctx.Handle(500, "Marshal", err)
@@ -452,7 +455,6 @@ func checkWebhook(ctx *middleware.Context) (*OrgRepoCtx, *models.Webhook) {
 	default:
 		ctx.Data["HookType"] = "gogs"
 	}
-	w.GetEvent()
 
 	ctx.Data["History"], err = w.History(1)
 	if err != nil {
@@ -530,7 +532,10 @@ func SlackHooksEditPost(ctx *middleware.Context, form auth.NewSlackHookForm) {
 	}
 
 	meta, err := json.Marshal(&models.SlackMeta{
-		Channel: form.Channel,
+		Channel:  form.Channel,
+		Username: form.Username,
+		IconURL:  form.IconURL,
+		Color:    form.Color,
 	})
 	if err != nil {
 		ctx.Handle(500, "Marshal", err)

+ 1 - 1
templates/repo/create.tmpl

@@ -38,7 +38,7 @@
           <div class="inline required field {{if .Err_RepoName}}error{{end}}">
             <label for="repo_name">{{.i18n.Tr "repo.repo_name"}}</label>
             <input id="repo_name" name="repo_name" value="{{.repo_name}}" autofocus required>
-            <span class="help">{{.i18n.Tr "repo.repo_name_helper"}}</span>
+            <span class="help">{{.i18n.Tr "repo.repo_name_helper" | Safe}}</span>
           </div>
           <div class="inline field">
             <label>{{.i18n.Tr "repo.visibility"}}</label>

+ 14 - 1
templates/repo/settings/hook_slack.tmpl

@@ -8,7 +8,20 @@
   </div>
   <div class="required field {{if .Err_Channel}}error{{end}}">
     <label for="channel">{{.i18n.Tr "repo.settings.slack_channel"}}</label>
-    <input id="channel" name="channel" value="{{.SlackHook.Channel}}" placeholder="#general" required>
+    <input id="channel" name="channel" value="{{.SlackHook.Channel}}" placeholder="e.g. #general" required>
+  </div>
+
+  <div class="field">
+    <label for="username">{{.i18n.Tr "repo.settings.slack_username"}}</label>
+    <input id="username" name="username" value="{{.SlackHook.Username}}" placeholder="e.g. Gogs">
+  </div>
+  <div class="field">
+    <label for="icon_url">{{.i18n.Tr "repo.settings.slack_icon_url"}}</label>
+    <input id="icon_url" name="icon_url" value="{{.SlackHook.IconURL}}" placeholder="e.g. https://example.com/img/favicon.png">
+  </div>
+  <div class="field">
+    <label for="color">{{.i18n.Tr "repo.settings.slack_color"}}</label>
+    <input id="color" name="color" value="{{.SlackHook.Color}}" placeholder="e.g. #dd4b39, good, warning, danger">
   </div>
 	{{template "repo/settings/hook_settings" .}}
 </form>

Some files were not shown because too many files changed in this diff