2014-05-05 20:52:25 -04:00
// Copyright 2014 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 models
import (
"encoding/json"
2014-05-05 21:36:08 -04:00
"errors"
2014-06-08 04:45:34 -04:00
"time"
2014-05-05 20:52:25 -04:00
2014-06-08 04:45:34 -04:00
"github.com/gogits/gogs/modules/httplib"
2014-05-05 20:52:25 -04:00
"github.com/gogits/gogs/modules/log"
2014-06-08 04:45:34 -04:00
"github.com/gogits/gogs/modules/setting"
2014-08-09 15:40:10 -07:00
"github.com/gogits/gogs/modules/uuid"
2014-05-05 20:52:25 -04:00
)
2014-05-05 21:36:08 -04:00
var (
ErrWebhookNotExist = errors . New ( "Webhook does not exist" )
)
2014-06-08 04:45:34 -04:00
type HookContentType int
2014-05-05 20:52:25 -04:00
const (
2014-06-08 04:45:34 -04:00
JSON HookContentType = iota + 1
FORM
2014-05-05 20:52:25 -04:00
)
2014-06-08 04:45:34 -04:00
// HookEvent represents events that will delivery hook.
2014-05-05 20:52:25 -04:00
type HookEvent struct {
PushOnly bool ` json:"push_only" `
}
2014-06-08 04:45:34 -04:00
// Webhook represents a web hook object.
2014-05-05 20:52:25 -04:00
type Webhook struct {
Id int64
RepoId int64
2014-05-06 11:50:31 -04:00
Url string ` xorm:"TEXT" `
2014-06-08 04:45:34 -04:00
ContentType HookContentType
2014-05-05 20:52:25 -04:00
Secret string ` xorm:"TEXT" `
Events string ` xorm:"TEXT" `
2014-05-05 21:36:08 -04:00
* HookEvent ` xorm:"-" `
2014-05-05 20:52:25 -04:00
IsSsl bool
IsActive bool
}
2014-06-08 04:54:52 -04:00
// GetEvent handles conversion from Events to HookEvent.
2014-05-05 21:36:08 -04:00
func ( w * Webhook ) GetEvent ( ) {
w . HookEvent = & HookEvent { }
if err := json . Unmarshal ( [ ] byte ( w . Events ) , w . HookEvent ) ; err != nil {
2014-07-26 00:24:27 -04:00
log . Error ( 4 , "webhook.GetEvent(%d): %v" , w . Id , err )
2014-05-05 20:52:25 -04:00
}
}
2014-06-08 04:54:52 -04:00
// UpdateEvent handles conversion from HookEvent to Events.
2014-06-08 04:45:34 -04:00
func ( w * Webhook ) UpdateEvent ( ) error {
2014-05-05 21:36:08 -04:00
data , err := json . Marshal ( w . HookEvent )
2014-05-05 20:52:25 -04:00
w . Events = string ( data )
return err
}
2014-06-08 04:54:52 -04:00
// HasPushEvent returns true if hook enbaled push event.
2014-05-06 11:50:31 -04:00
func ( w * Webhook ) HasPushEvent ( ) bool {
if w . PushOnly {
return true
}
return false
}
2014-06-08 04:45:34 -04:00
// CreateWebhook creates a new web hook.
2014-05-05 20:52:25 -04:00
func CreateWebhook ( w * Webhook ) error {
2014-06-21 00:51:41 -04:00
_ , err := x . Insert ( w )
2014-05-05 20:52:25 -04:00
return err
}
2014-05-05 21:36:08 -04:00
// GetWebhookById returns webhook by given ID.
func GetWebhookById ( hookId int64 ) ( * Webhook , error ) {
w := & Webhook { Id : hookId }
2014-06-21 00:51:41 -04:00
has , err := x . Get ( w )
2014-05-05 21:36:08 -04:00
if err != nil {
return nil , err
} else if ! has {
return nil , ErrWebhookNotExist
}
return w , nil
}
2014-05-06 11:50:31 -04:00
// GetActiveWebhooksByRepoId returns all active webhooks of repository.
func GetActiveWebhooksByRepoId ( repoId int64 ) ( ws [ ] * Webhook , err error ) {
2014-06-21 00:51:41 -04:00
err = x . Find ( & ws , & Webhook { RepoId : repoId , IsActive : true } )
2014-05-06 11:50:31 -04:00
return ws , err
}
2014-05-05 20:52:25 -04:00
// GetWebhooksByRepoId returns all webhooks of repository.
func GetWebhooksByRepoId ( repoId int64 ) ( ws [ ] * Webhook , err error ) {
2014-06-21 00:51:41 -04:00
err = x . Find ( & ws , & Webhook { RepoId : repoId } )
2014-05-05 20:52:25 -04:00
return ws , err
}
2014-05-05 21:36:08 -04:00
2014-06-08 04:45:34 -04:00
// UpdateWebhook updates information of webhook.
func UpdateWebhook ( w * Webhook ) error {
2014-06-21 00:51:41 -04:00
_ , err := x . AllCols ( ) . Update ( w )
2014-06-08 04:45:34 -04:00
return err
}
2014-05-05 21:36:08 -04:00
// DeleteWebhook deletes webhook of repository.
func DeleteWebhook ( hookId int64 ) error {
2014-06-21 00:51:41 -04:00
_ , err := x . Delete ( & Webhook { Id : hookId } )
2014-05-05 21:36:08 -04:00
return err
}
2014-06-08 04:45:34 -04:00
// ___ ___ __ ___________ __
// / | \ ____ ____ | | _\__ ___/____ _____| | __
// / ~ \/ _ \ / _ \| |/ / | | \__ \ / ___/ |/ /
// \ Y ( <_> | <_> ) < | | / __ \_\___ \| <
// \___|_ / \____/ \____/|__|_ \ |____| (____ /____ >__|_ \
// \/ \/ \/ \/ \/
type HookTaskType int
const (
2014-06-08 04:54:52 -04:00
WEBHOOK HookTaskType = iota + 1
2014-06-08 04:45:34 -04:00
SERVICE
)
2014-08-09 15:40:10 -07:00
type HookEventType string
const (
PUSH HookEventType = "push"
)
2014-06-08 04:45:34 -04:00
type PayloadAuthor struct {
Name string ` json:"name" `
Email string ` json:"email" `
}
type PayloadCommit struct {
Id string ` json:"id" `
Message string ` json:"message" `
Url string ` json:"url" `
Author * PayloadAuthor ` json:"author" `
}
type PayloadRepo struct {
Id int64 ` json:"id" `
Name string ` json:"name" `
Url string ` json:"url" `
Description string ` json:"description" `
Website string ` json:"website" `
Watchers int ` json:"watchers" `
Owner * PayloadAuthor ` json:"author" `
Private bool ` json:"private" `
}
2014-06-08 04:54:52 -04:00
// Payload represents a payload information of hook.
2014-06-08 04:45:34 -04:00
type Payload struct {
Secret string ` json:"secret" `
Ref string ` json:"ref" `
Commits [ ] * PayloadCommit ` json:"commits" `
Repo * PayloadRepo ` json:"repository" `
Pusher * PayloadAuthor ` json:"pusher" `
}
2014-06-08 04:54:52 -04:00
// HookTask represents a hook task.
2014-06-08 04:45:34 -04:00
type HookTask struct {
Id int64
2014-08-09 15:40:10 -07:00
Uuid string
2014-06-08 04:54:52 -04:00
Type HookTaskType
2014-06-08 04:45:34 -04:00
Url string
* Payload ` xorm:"-" `
PayloadContent string ` xorm:"TEXT" `
ContentType HookContentType
2014-08-09 15:40:10 -07:00
EventType HookEventType
2014-06-08 04:45:34 -04:00
IsSsl bool
IsDeliveried bool
2014-08-09 15:40:10 -07:00
IsSucceed bool
2014-06-08 04:45:34 -04:00
}
// CreateHookTask creates a new hook task,
// it handles conversion from Payload to PayloadContent.
func CreateHookTask ( t * HookTask ) error {
data , err := json . Marshal ( t . Payload )
if err != nil {
return err
}
2014-08-09 15:40:10 -07:00
t . Uuid = uuid . NewV4 ( ) . String ( )
2014-06-08 04:45:34 -04:00
t . PayloadContent = string ( data )
2014-06-21 00:51:41 -04:00
_ , err = x . Insert ( t )
2014-06-08 04:45:34 -04:00
return err
}
// UpdateHookTask updates information of hook task.
func UpdateHookTask ( t * HookTask ) error {
2014-06-21 00:51:41 -04:00
_ , err := x . AllCols ( ) . Update ( t )
2014-06-08 04:45:34 -04:00
return err
}
// DeliverHooks checks and delivers undelivered hooks.
func DeliverHooks ( ) {
timeout := time . Duration ( setting . WebhookDeliverTimeout ) * time . Second
2014-06-21 00:51:41 -04:00
x . Where ( "is_deliveried=?" , false ) . Iterate ( new ( HookTask ) ,
2014-06-08 04:45:34 -04:00
func ( idx int , bean interface { } ) error {
t := bean . ( * HookTask )
2014-08-09 15:40:10 -07:00
req := httplib . Post ( t . Url ) . SetTimeout ( timeout , timeout ) .
Header ( "X-Gogs-Delivery" , t . Uuid ) .
Header ( "X-Gogs-Event" , string ( t . EventType ) )
switch t . ContentType {
case JSON :
req = req . Header ( "Content-Type" , "application/json" ) . Body ( t . PayloadContent )
case FORM :
req . Param ( "payload" , t . PayloadContent )
2014-06-08 04:45:34 -04:00
}
t . IsDeliveried = true
2014-08-09 15:40:10 -07:00
// TODO: record response.
if _ , err := req . Response ( ) ; err != nil {
log . Error ( 4 , "Delivery: %v" , err )
} else {
t . IsSucceed = true
}
2014-06-08 04:45:34 -04:00
if err := UpdateHookTask ( t ) ; err != nil {
2014-08-09 15:40:10 -07:00
log . Error ( 4 , "UpdateHookTask: %v" , err )
2014-06-08 04:45:34 -04:00
return nil
}
2014-08-09 15:40:10 -07:00
log . Trace ( "Hook delivered(%s): %s" , t . Uuid , t . PayloadContent )
2014-06-08 04:45:34 -04:00
return nil
} )
}