Add support for MS Teams webhooks (#6632)

This commit is contained in:
Daniel Grier
2019-04-20 00:18:06 +10:00
committed by techknowlogick
parent 2af67f6044
commit b9d1fb6de3
14 changed files with 825 additions and 1 deletions

View File

@ -122,4 +122,5 @@ _Symbols used in table:_
| Two factor authentication (2FA) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ |
| Mattermost/Slack integration | ✓ | ✓ | | ✓ | ✓ | | ✓ |
| Discord integration | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ | ✘ |
| Microsoft Teams integration | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
| External CI/CD status display | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |

View File

@ -466,6 +466,7 @@ const (
DISCORD
DINGTALK
TELEGRAM
MSTEAMS
)
var hookTaskTypes = map[string]HookTaskType{
@ -475,6 +476,7 @@ var hookTaskTypes = map[string]HookTaskType{
"discord": DISCORD,
"dingtalk": DINGTALK,
"telegram": TELEGRAM,
"msteams": MSTEAMS,
}
// ToHookTaskType returns HookTaskType by given name.
@ -497,6 +499,8 @@ func (t HookTaskType) Name() string {
return "dingtalk"
case TELEGRAM:
return "telegram"
case MSTEAMS:
return "msteams"
}
return ""
}
@ -675,6 +679,11 @@ func prepareWebhook(e Engine, w *Webhook, repo *Repository, event HookEventType,
if err != nil {
return fmt.Errorf("GetTelegramPayload: %v", err)
}
case MSTEAMS:
payloader, err = GetMSTeamsPayload(p, event, w.Meta)
if err != nil {
return fmt.Errorf("GetMSTeamsPayload: %v", err)
}
default:
p.SetSecret(w.Secret)
payloader = p

703
models/webhook_msteams.go Normal file

File diff suppressed because it is too large Load Diff

View File

@ -275,6 +275,17 @@ func (f *NewTelegramHookForm) Validate(ctx *macaron.Context, errs binding.Errors
return validate(errs, ctx.Data, f, ctx.Locale)
}
// NewMSTeamsHookForm form for creating MS Teams hook
type NewMSTeamsHookForm struct {
PayloadURL string `binding:"Required;ValidUrl"`
WebhookForm
}
// Validate validates the fields
func (f *NewMSTeamsHookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}
// .___
// | | ______ ________ __ ____
// | |/ ___// ___/ | \_/ __ \

View File

@ -25,6 +25,6 @@ func newWebhookService() {
Webhook.QueueLength = sec.Key("QUEUE_LENGTH").MustInt(1000)
Webhook.DeliverTimeout = sec.Key("DELIVER_TIMEOUT").MustInt(5)
Webhook.SkipTLSVerify = sec.Key("SKIP_TLS_VERIFY").MustBool()
Webhook.Types = []string{"gitea", "gogs", "slack", "discord", "dingtalk", "telegram"}
Webhook.Types = []string{"gitea", "gogs", "slack", "discord", "dingtalk", "telegram", "msteams"}
Webhook.PagingNum = sec.Key("PAGING_NUM").MustInt(10)
}

View File

@ -1212,6 +1212,7 @@ settings.slack_channel = Channel
settings.add_discord_hook_desc = Integrate <a href="%s">Discord</a> into your repository.
settings.add_dingtalk_hook_desc = Integrate <a href="%s">Dingtalk</a> into your repository.
settings.add_telegram_hook_desc = Integrate <a href="%s">Telegram</a> into your repository.
settings.add_msteams_hook_desc = Integrate <a href="%s">Microsoft Teams</a> into your repository.
settings.deploy_keys = Deploy Keys
settings.add_deploy_key = Add Deploy Key
settings.deploy_key_desc = Deploy keys have read-only pull access to the repository.

BIN
public/img/msteams.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@ -380,6 +380,46 @@ func TelegramHooksNewPost(ctx *context.Context, form auth.NewTelegramHookForm) {
ctx.Redirect(orCtx.Link)
}
// MSTeamsHooksNewPost response for creating MS Teams hook
func MSTeamsHooksNewPost(ctx *context.Context, form auth.NewMSTeamsHookForm) {
ctx.Data["Title"] = ctx.Tr("repo.settings")
ctx.Data["PageIsSettingsHooks"] = true
ctx.Data["PageIsSettingsHooksNew"] = true
ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
orCtx, err := getOrgRepoCtx(ctx)
if err != nil {
ctx.ServerError("getOrgRepoCtx", err)
return
}
if ctx.HasError() {
ctx.HTML(200, orCtx.NewTemplate)
return
}
w := &models.Webhook{
RepoID: orCtx.RepoID,
URL: form.PayloadURL,
ContentType: models.ContentTypeJSON,
HookEvent: ParseHookEvent(form.WebhookForm),
IsActive: form.Active,
HookTaskType: models.MSTEAMS,
Meta: "",
OrgID: orCtx.OrgID,
}
if err := w.UpdateEvent(); err != nil {
ctx.ServerError("UpdateEvent", err)
return
} else if err := models.CreateWebhook(w); err != nil {
ctx.ServerError("CreateWebhook", err)
return
}
ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
ctx.Redirect(orCtx.Link)
}
// SlackHooksNewPost response for creating slack hook
func SlackHooksNewPost(ctx *context.Context, form auth.NewSlackHookForm) {
ctx.Data["Title"] = ctx.Tr("repo.settings")
@ -738,6 +778,38 @@ func TelegramHooksEditPost(ctx *context.Context, form auth.NewTelegramHookForm)
ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
}
// MSTeamsHooksEditPost response for editing MS Teams hook
func MSTeamsHooksEditPost(ctx *context.Context, form auth.NewMSTeamsHookForm) {
ctx.Data["Title"] = ctx.Tr("repo.settings")
ctx.Data["PageIsSettingsHooks"] = true
ctx.Data["PageIsSettingsHooksEdit"] = true
orCtx, w := checkWebhook(ctx)
if ctx.Written() {
return
}
ctx.Data["Webhook"] = w
if ctx.HasError() {
ctx.HTML(200, orCtx.NewTemplate)
return
}
w.URL = form.PayloadURL
w.HookEvent = ParseHookEvent(form.WebhookForm)
w.IsActive = form.Active
if err := w.UpdateEvent(); err != nil {
ctx.ServerError("UpdateEvent", err)
return
} else if err := models.UpdateWebhook(w); err != nil {
ctx.ServerError("UpdateWebhook", err)
return
}
ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
}
// TestWebhook test if web hook is work fine
func TestWebhook(ctx *context.Context) {
hookID := ctx.ParamsInt64(":id")

View File

@ -422,12 +422,14 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost)
m.Post("/discord/new", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksNewPost)
m.Post("/dingtalk/new", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksNewPost)
m.Post("/msteams/new", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost)
m.Get("/:id", repo.WebHooksEdit)
m.Post("/gitea/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost)
m.Post("/gogs/:id", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksEditPost)
m.Post("/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost)
m.Post("/discord/:id", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksEditPost)
m.Post("/dingtalk/:id", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost)
m.Post("/msteams/:id", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost)
})
m.Group("/auths", func() {
@ -615,6 +617,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/discord/new", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksNewPost)
m.Post("/dingtalk/new", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksNewPost)
m.Post("/telegram/new", bindIgnErr(auth.NewTelegramHookForm{}), repo.TelegramHooksNewPost)
m.Post("/msteams/new", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost)
m.Get("/:id", repo.WebHooksEdit)
m.Post("/:id/test", repo.TestWebhook)
m.Post("/gitea/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost)
@ -623,6 +626,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/discord/:id", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksEditPost)
m.Post("/dingtalk/:id", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost)
m.Post("/telegram/:id", bindIgnErr(auth.NewTelegramHookForm{}), repo.TelegramHooksEditPost)
m.Post("/msteams/:id", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost)
m.Group("/git", func() {
m.Get("", repo.GitHooks)

View File

@ -20,6 +20,8 @@
<img class="img-13" src="{{AppSubUrl}}/img/discord.png">
{{else if eq .HookType "dingtalk"}}
<img class="img-13" src="{{AppSubUrl}}/img/dingtalk.ico">
{{else if eq .HookType "msteams"}}
<img class="img-13" src="{{AppSubUrl}}/img/msteams.png">
{{end}}
</div>
</h4>
@ -29,6 +31,7 @@
{{template "repo/settings/webhook/slack" .}}
{{template "repo/settings/webhook/discord" .}}
{{template "repo/settings/webhook/dingtalk" .}}
{{template "repo/settings/webhook/msteams" .}}
</div>
{{template "repo/settings/webhook/history" .}}

View File

@ -21,6 +21,8 @@
<img class="img-13" src="{{AppSubUrl}}/img/dingtalk.png">
{{else if eq .HookType "telegram"}}
<img class="img-13" src="{{AppSubUrl}}/img/telegram.png">
{{else if eq .HookType "msteams"}}
<img class="img-13" src="{{AppSubUrl}}/img/msteams.png">
{{end}}
</div>
</h4>
@ -31,6 +33,7 @@
{{template "repo/settings/webhook/discord" .}}
{{template "repo/settings/webhook/dingtalk" .}}
{{template "repo/settings/webhook/telegram" .}}
{{template "repo/settings/webhook/msteams" .}}
</div>
{{template "repo/settings/webhook/history" .}}

View File

@ -23,6 +23,9 @@
<a class="item" href="{{.BaseLink}}/telegram/new">
<img class="img-10" src="{{AppSubUrl}}/img/telegram.png">Telegram
</a>
<a class="item" href="{{.BaseLink}}/msteams/new">
<img class="img-10" src="{{AppSubUrl}}/img/msteams.png">Microsoft Teams
</a>
</div>
</div>
</div>

View File

@ -0,0 +1,11 @@
{{if eq .HookType "msteams"}}
<p>{{.i18n.Tr "repo.settings.add_msteams_hook_desc" "https://teams.microsoft.com" | Str2html}}</p>
<form class="ui form" action="{{.BaseLink}}/msteams/{{or .Webhook.ID "new"}}" method="post">
{{.CsrfTokenHtml}}
<div class="required field {{if .Err_PayloadURL}}error{{end}}">
<label for="payload_url">{{.i18n.Tr "repo.settings.payload_url"}}</label>
<input id="payload_url" name="payload_url" type="url" value="{{.Webhook.URL}}" autofocus required>
</div>
{{template "repo/settings/webhook/settings" .}}
</form>
{{end}}

View File

@ -19,6 +19,8 @@
<img class="img-13" src="{{AppSubUrl}}/img/dingtalk.ico">
{{else if eq .HookType "telegram"}}
<img class="img-13" src="{{AppSubUrl}}/img/telegram.png">
{{else if eq .HookType "msteams"}}
<img class="img-13" src="{{AppSubUrl}}/img/msteams.png">
{{end}}
</div>
</h4>
@ -29,6 +31,7 @@
{{template "repo/settings/webhook/discord" .}}
{{template "repo/settings/webhook/dingtalk" .}}
{{template "repo/settings/webhook/telegram" .}}
{{template "repo/settings/webhook/msteams" .}}
</div>
{{template "repo/settings/webhook/history" .}}