はじめに
- ゆるふわです
あわせて読みたい
- backlogのgitからjenkinsをbuild - Qiita http://qiita.com/shinderuman@github/items/eff9f3567cbd11c03ee2
ゴール
- backlogのwebhookのイベント(pull-request作成、更新、コメント)をサーバー上のgolangで受け付けてslackのincoming webhookにリクエストする
公式ドキュメントでjson定義探す
Webhookでは、指定されたURLにデータをJSON形式でPUSHで送信します。JSONデータの属性は一部の共通部分を除き、イベントによって異なるためテスト実行等で確認して下さい。
><
自鯖に流して確認
pull-request作成
{
"content": {
"changes": [],
"comment": null,
"description": "XXX",
"diff": null,
"id": 111,
"issue": {
"description": "XXX",
"id": 111,
"key_id": 111,
"summary": "XXX"
},
"number": 1,
"repository": {
"description": "",
"id": 111,
"name": "test"
},
"summary": "XXX"
},
"created": "2016-04-14T14:31:33Z",
"createdUser": {
"id": 111,
"mailAddress": null,
"name": "XXX",
"nulabAccount": null,
"roleType": 1,
"userId": null
},
"id": 111,
"notifications": [
{
"alreadyRead": false,
"id": 111,
"reason": 111,
"resourceAlreadyRead": false,
"user": {
"id": 111,
"mailAddress": null,
"name": "XXX",
"nulabAccount": null,
"roleType": 111,
"userId": null
}
},
{
"alreadyRead": false,
"id": 111,
"reason": 111,
"resourceAlreadyRead": false,
"user": {
"id": 111,
"mailAddress": null,
"name": "XXX",
"nulabAccount": {
"name": "XXX",
"nulabId": "XXX",
"uniqueId": "XXX"
},
"roleType": 111,
"userId": null
}
}
],
"project": {
"archived": false,
"chartEnabled": true,
"id": 111,
"name": "XXX",
"projectKey": "XXX",
"subtaskingEnabled": true
},
"type": 111
}
pull-request更新
{
"content": {
"changes": [
{
"field": "description",
"new_value": "XXX",
"old_value": "XXX"
}
],
"comment": {
"content": "",
"id": 111
},
"description": "XXX",
"diff": "",
"id": 111,
"issue": {
"description": "XXX",
"id": 111,
"key_id": 111,
"summary": "XXX"
},
"number": 111,
"repository": {
"description": "",
"id": 111,
"name": "XXX"
},
"summary": "XXX"
},
"created": "2016-04-14T14:32:30Z",
"createdUser": {
"id": 111,
"mailAddress": null,
"name": "XXX",
"nulabAccount": null,
"roleType": 111,
"userId": null
},
"id": 111,
"notifications": [],
"project": {
"archived": false,
"chartEnabled": true,
"id": 111,
"name": "XXX",
"projectKey": "XXX",
"subtaskingEnabled": true
},
"type": 111
}
pull-requestコメント
{
"content": {
"changes": [],
"comment": {
"content": "XXX",
"id": 111
},
"description": "XXX",
"diff": null,
"id": 111,
"issue": {
"description": "XXX",
"id": 111,
"key_id": 111,
"summary": "XXX"
},
"number": 111,
"repository": {
"description": "",
"id": 111,
"name": "XXX"
},
"summary": "XXX"
},
"created": "2016-04-14T14:32:04Z",
"createdUser": {
"id": 111,
"mailAddress": null,
"name": "XXX",
"nulabAccount": null,
"roleType": 111,
"userId": null
},
"id": 111,
"notifications": [
{
"alreadyRead": false,
"id": 111,
"reason": 111,
"resourceAlreadyRead": false,
"user": {
"id": 111,
"mailAddress": null,
"name": "XXX",
"nulabAccount": null,
"roleType": 111,
"userId": null
}
},
{
"alreadyRead": false,
"id": 111,
"reason": 111,
"resourceAlreadyRead": false,
"user": {
"id": 111,
"mailAddress": null,
"name": "XXX",
"nulabAccount": {
"name": "XXX",
"nulabId": "XXX",
"uniqueId": "XXX"
},
"roleType": 111,
"userId": null
}
}
],
"project": {
"archived": false,
"chartEnabled": true,
"id": 111,
"name": "XXX",
"projectKey": "XXX",
"subtaskingEnabled": true
},
"type": 111
}
上記3つをmergeしたstruct
type Backlog struct {
Content struct {
Changes []interface{} `json:"changes"`
Comment struct {
Content string `json:"content"`
ID int `json:"id"`
} `json:"comment"`
Description string `json:"description"`
Diff interface{} `json:"diff"`
ID int `json:"id"`
Issue struct {
Description string `json:"description"`
ID int `json:"id"`
KeyID int `json:"key_id"`
Summary string `json:"summary"`
} `json:"issue"`
Number int `json:"number"`
Repository struct {
Description string `json:"description"`
ID int `json:"id"`
Name string `json:"name"`
} `json:"repository"`
Summary string `json:"summary"`
} `json:"content"`
Created string `json:"created"`
CreatedUser struct {
ID int `json:"id"`
MailAddress interface{} `json:"mailAddress"`
Name string `json:"name"`
NulabAccount interface{} `json:"nulabAccount"`
RoleType int `json:"roleType"`
UserID interface{} `json:"userId"`
} `json:"createdUser"`
ID int `json:"id"`
Notifications []struct {
AlreadyRead bool `json:"alreadyRead"`
ID int `json:"id"`
Reason int `json:"reason"`
ResourceAlreadyRead bool `json:"resourceAlreadyRead"`
User struct {
ID int `json:"id"`
MailAddress interface{} `json:"mailAddress"`
Name string `json:"name"`
NulabAccount interface{} `json:"nulabAccount"`
RoleType int `json:"roleType"`
UserID interface{} `json:"userId"`
} `json:"user"`
} `json:"notifications"`
Project struct {
Archived bool `json:"archived"`
ChartEnabled bool `json:"chartEnabled"`
ID int `json:"id"`
Name string `json:"name"`
ProjectKey string `json:"projectKey"`
SubtaskingEnabled bool `json:"subtaskingEnabled"`
} `json:"project"`
Type int `json:"type"`
}
実装
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
)
type Slack struct {
Channel string `json:"channel"`
IconEmoji string `json:"icon_emoji"`
IconUrl string `json:"icon_url"`
Text string `json:"text"`
Username string `json:"username"`
}
type Backlog struct {
Content struct {
Changes []interface{} `json:"changes"`
Comment struct {
Content string `json:"content"`
ID int `json:"id"`
} `json:"comment"`
Description string `json:"description"`
Diff interface{} `json:"diff"`
ID int `json:"id"`
Issue struct {
Description string `json:"description"`
ID int `json:"id"`
KeyID int `json:"key_id"`
Summary string `json:"summary"`
} `json:"issue"`
Number int `json:"number"`
Repository struct {
Description string `json:"description"`
ID int `json:"id"`
Name string `json:"name"`
} `json:"repository"`
Summary string `json:"summary"`
} `json:"content"`
Created string `json:"created"`
CreatedUser struct {
ID int `json:"id"`
MailAddress interface{} `json:"mailAddress"`
Name string `json:"name"`
NulabAccount interface{} `json:"nulabAccount"`
RoleType int `json:"roleType"`
UserID interface{} `json:"userId"`
} `json:"createdUser"`
ID int `json:"id"`
Notifications []struct {
AlreadyRead bool `json:"alreadyRead"`
ID int `json:"id"`
Reason int `json:"reason"`
ResourceAlreadyRead bool `json:"resourceAlreadyRead"`
User struct {
ID int `json:"id"`
MailAddress interface{} `json:"mailAddress"`
Name string `json:"name"`
NulabAccount interface{} `json:"nulabAccount"`
RoleType int `json:"roleType"`
UserID interface{} `json:"userId"`
} `json:"user"`
} `json:"notifications"`
Project struct {
Archived bool `json:"archived"`
ChartEnabled bool `json:"chartEnabled"`
ID int `json:"id"`
Name string `json:"name"`
ProjectKey string `json:"projectKey"`
SubtaskingEnabled bool `json:"subtaskingEnabled"`
} `json:"project"`
Type int `json:"type"`
}
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
var b Backlog
err := json.NewDecoder(r.Body).Decode(&b)
if !(err != nil && err == io.EOF) {
s := Slack{Text: fmt.Sprintf("https://{YOUR_BACKLOG_DOMAIN}.backlog.jp/git/%s/%s/pullRequests/%d", b.Project.ProjectKey, b.Content.Repository.Name, b.Content.Number)}
j, _ := json.Marshal(s)
http.PostForm("https://hooks.slack.com/services/XXXXX/XXXXX/XXXXXXX", url.Values{"payload": {string(j)}})
}
})
http.ListenAndServe(":80", nil)
}
URL送ったところで力尽きたorz
感想戦
- json調べるのにほぼほぼの時間を使った
- プロジェクト配下のすべてのリポジトリのpull-requestがwebhookで送られてくるので不要なものを無視するのがひと手間
- コメントごとにslackに通知が来るのはだいぶノイズなのでn分間隔でpoolして重複は除去して通知とかやりたい(やるとはいってない