From fe7552fcf473b3e7fe42f0c5f37d7ed6db175f08 Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Sat, 8 Apr 2017 11:40:22 -0400 Subject: [PATCH] Add GitLab support --- README.md | 17 +- gitlab/gitlab.go | 144 ++++++ gitlab/gitlab_test.go | 1124 +++++++++++++++++++++++++++++++++++++++++ gitlab/payload.go | 404 +++++++++++++++ webhooks.go | 7 +- webhooks_test.go | 1 + 6 files changed, 1684 insertions(+), 13 deletions(-) create mode 100644 gitlab/gitlab.go create mode 100644 gitlab/gitlab_test.go create mode 100644 gitlab/payload.go diff --git a/README.md b/README.md index d168e0f..c38130e 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Library webhooks [![GoDoc](https://godoc.org/gopkg.in/go-playground/webhooks.v2?status.svg)](https://godoc.org/gopkg.in/go-playground/webhooks.v2) ![License](https://img.shields.io/dub/l/vibe-d.svg) -Library webhooks allows for easy recieving and parsing of GitHub & Bitbucket Webhook Events +Library webhooks allows for easy recieving and parsing of GitHub, Bitbucket and GitLab Webhook Events Features: @@ -16,7 +16,7 @@ Features: Notes: -* Github - Currently only accepting json payloads. +* Currently only accepting json payloads. Installation ------------ @@ -27,11 +27,11 @@ Use go get. go get -u gopkg.in/go-playground/webhooks.v2 ``` -Then import the validator package into your own code. +Then import the package into your own code. import "gopkg.in/go-playground/webhooks.v2" -Usage and documentation +Usage and Documentation ------ Please see http://godoc.org/gopkg.in/go-playground/webhooks.v2 for detailed usage docs. @@ -147,14 +147,9 @@ func HandleMultiple(payload interface{}, header webhooks.Header) { Contributing ------ -Pull requests for other service like BitBucket are welcome! +Pull requests for other services are welcome! -There will always be a development branch for each version i.e. `v1-development`. In order to contribute, -please make your pull requests against those branches. - -If the changes being proposed or requested are breaking changes, please create an issue, for discussion -or create a pull request against the highest development branch for example this package has a -v1 and v1-development branch however, there will also be a v2-development branch even though v2 doesn't exist yet. +If the changes being proposed or requested are breaking changes, please create an issue for discussion. License ------ diff --git a/gitlab/gitlab.go b/gitlab/gitlab.go new file mode 100644 index 0000000..d76922a --- /dev/null +++ b/gitlab/gitlab.go @@ -0,0 +1,144 @@ +package gitlab + +import ( + "encoding/json" + "io/ioutil" + "net/http" + + "gopkg.in/go-playground/webhooks.v2" +) + +// Webhook instance contains all methods needed to process events +type Webhook struct { + provider webhooks.Provider + secret string + eventFuncs map[Event]webhooks.ProcessPayloadFunc +} + +// Config defines the configuration to create a new GitHub Webhook instance +type Config struct { + Secret string +} + +// Event defines a GitHub hook event type +type Event string + +// GitLab hook types +const ( + PushEvents Event = "Push Hook" + TagEvents Event = "Tag Push Hook" + IssuesEvents Event = "Issue Hook" + CommentEvents Event = "Note Hook" + MergerRequestEvents Event = "Merge Request Hook" + WikiPageEvents Event = "Wiki Page Hook" + PipelineEvents Event = "Pipeline Hook" + BuildEvents Event = "Build Hook" +) + +// New creates and returns a WebHook instance denoted by the Provider type +func New(config *Config) *Webhook { + return &Webhook{ + provider: webhooks.GitLab, + secret: config.Secret, + eventFuncs: map[Event]webhooks.ProcessPayloadFunc{}, + } +} + +// Provider returns the current hooks provider ID +func (hook Webhook) Provider() webhooks.Provider { + return hook.provider +} + +// RegisterEvents registers the function to call when the specified event(s) are encountered +func (hook Webhook) RegisterEvents(fn webhooks.ProcessPayloadFunc, events ...Event) { + + for _, event := range events { + hook.eventFuncs[event] = fn + } +} + +// ParsePayload parses and verifies the payload and fires off the mapped function, if it exists. +func (hook Webhook) ParsePayload(w http.ResponseWriter, r *http.Request) { + + event := r.Header.Get("X-Gitlab-Event") + if len(event) == 0 { + http.Error(w, "400 Bad Request - Missing X-Gitlab-Event Header", http.StatusBadRequest) + return + } + + gitLabEvent := Event(event) + + fn, ok := hook.eventFuncs[gitLabEvent] + // if no event registered + if !ok { + return + } + + payload, err := ioutil.ReadAll(r.Body) + if err != nil || len(payload) == 0 { + http.Error(w, "Error reading Body", http.StatusInternalServerError) + return + } + + // If we have a Secret set, we should check the MAC + if len(hook.secret) > 0 { + + signature := r.Header.Get("X-Gitlab-Token") + + if signature != hook.secret { + http.Error(w, "403 Forbidden - Token missmatch", http.StatusForbidden) + return + } + } + + // Make headers available to ProcessPayloadFunc as a webhooks type + hd := webhooks.Header(r.Header) + + switch gitLabEvent { + case PushEvents: + var pe PushEventPayload + json.Unmarshal([]byte(payload), &pe) + hook.runProcessPayloadFunc(fn, pe, hd) + + case TagEvents: + var te TagEventPayload + json.Unmarshal([]byte(payload), &te) + hook.runProcessPayloadFunc(fn, te, hd) + + case IssuesEvents: + var ie IssueEventPayload + json.Unmarshal([]byte(payload), &ie) + hook.runProcessPayloadFunc(fn, ie, hd) + + case CommentEvents: + var ce CommentEventPayload + json.Unmarshal([]byte(payload), &ce) + hook.runProcessPayloadFunc(fn, ce, hd) + + case MergerRequestEvents: + var mre MergeRequestEventPayload + json.Unmarshal([]byte(payload), &mre) + hook.runProcessPayloadFunc(fn, mre, hd) + + case WikiPageEvents: + var wpe WikiPageEventPayload + json.Unmarshal([]byte(payload), &wpe) + hook.runProcessPayloadFunc(fn, wpe, hd) + + case PipelineEvents: + var pe PipelineEventPayload + json.Unmarshal([]byte(payload), &pe) + hook.runProcessPayloadFunc(fn, pe, hd) + + case BuildEvents: + var be BuildEventPayload + json.Unmarshal([]byte(payload), &be) + hook.runProcessPayloadFunc(fn, be, hd) + } +} + +func (hook Webhook) runProcessPayloadFunc(fn webhooks.ProcessPayloadFunc, results interface{}, header webhooks.Header) { + go func(fn webhooks.ProcessPayloadFunc, results interface{}, header webhooks.Header) { + fn(results, header) + }(fn, results, header) +} diff --git a/gitlab/gitlab_test.go b/gitlab/gitlab_test.go new file mode 100644 index 0000000..20e81ff --- /dev/null +++ b/gitlab/gitlab_test.go @@ -0,0 +1,1124 @@ +package gitlab + +import ( + "bytes" + "net/http" + "os" + "strconv" + "testing" + "time" + + . "gopkg.in/go-playground/assert.v1" + "gopkg.in/go-playground/webhooks.v2" +) + +// NOTES: +// - Run "go test" to run tests +// - Run "gocov test | gocov report" to report on test converage by file +// - Run "gocov test | gocov annotate -" to report on all code and functions, those ,marked with "MISS" were never called +// +// or +// +// -- may be a good idea to change to output path to somewherelike /tmp +// go test -coverprofile cover.out && go tool cover -html=cover.out -o cover.html +// + +const ( + port = 3009 + path = "/webhooks" +) + +// HandlePayload handles GitHub event(s) +func HandlePayload(payload interface{}, header webhooks.Header) { + +} + +var hook *Webhook + +func TestMain(m *testing.M) { + + // setup + hook = New(&Config{Secret: "sampleToken!"}) + hook.RegisterEvents(HandlePayload, + PushEvents, + TagEvents, + IssuesEvents, + CommentEvents, + MergerRequestEvents, + WikiPageEvents, + PipelineEvents, + BuildEvents, + ) + go webhooks.Run(hook, "127.0.0.1:"+strconv.Itoa(port), path) + time.Sleep(5000) + + os.Exit(m.Run()) + + // teardown +} + +func TestProvider(t *testing.T) { + Equal(t, hook.Provider(), webhooks.GitLab) +} + +func TestBadNoEventHeader(t *testing.T) { + payload := "{}" + + req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload))) + req.Header.Set("Content-Type", "application/json") + + Equal(t, err, nil) + + client := &http.Client{} + resp, err := client.Do(req) + Equal(t, err, nil) + + defer resp.Body.Close() + + Equal(t, resp.StatusCode, http.StatusBadRequest) +} + +func TestUnsubscribedEvent(t *testing.T) { + payload := "{}" + + req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload))) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("X-Gitlab-Event", "noneexistant_event") + + Equal(t, err, nil) + + client := &http.Client{} + resp, err := client.Do(req) + Equal(t, err, nil) + + defer resp.Body.Close() + + Equal(t, resp.StatusCode, http.StatusOK) +} + +func TestBadBody(t *testing.T) { + payload := "" + + req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload))) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("X-Gitlab-Event", "Note Hook") + + Equal(t, err, nil) + + client := &http.Client{} + resp, err := client.Do(req) + Equal(t, err, nil) + + defer resp.Body.Close() + + Equal(t, resp.StatusCode, http.StatusInternalServerError) +} + +func TestTokenMissmatch(t *testing.T) { + payload := "{}" + + req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload))) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("X-Gitlab-Event", "Note Hook") + req.Header.Set("X-Gitlab-Token", "itsnotasampleToken!") + + Equal(t, err, nil) + + client := &http.Client{} + resp, err := client.Do(req) + Equal(t, err, nil) + + defer resp.Body.Close() + + Equal(t, resp.StatusCode, http.StatusForbidden) +} + +func TestPushEvent(t *testing.T) { + + payload := `{ + "object_kind": "push", + "before": "95790bf891e76fee5e1747ab589903a6a1f80f22", + "after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", + "ref": "refs/heads/master", + "checkout_sha": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", + "user_id": 4, + "user_name": "John Smith", + "user_email": "john@example.com", + "user_avatar": "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80", + "project_id": 15, + "project":{ + "name":"Diaspora", + "description":"", + "web_url":"http://example.com/mike/diaspora", + "avatar_url":null, + "git_ssh_url":"git@example.com:mike/diaspora.git", + "git_http_url":"http://example.com/mike/diaspora.git", + "namespace":"Mike", + "visibility_level":0, + "path_with_namespace":"mike/diaspora", + "default_branch":"master", + "homepage":"http://example.com/mike/diaspora", + "url":"git@example.com:mike/diaspora.git", + "ssh_url":"git@example.com:mike/diaspora.git", + "http_url":"http://example.com/mike/diaspora.git" + }, + "repository":{ + "name": "Diaspora", + "url": "git@example.com:mike/diaspora.git", + "description": "", + "homepage": "http://example.com/mike/diaspora", + "git_http_url":"http://example.com/mike/diaspora.git", + "git_ssh_url":"git@example.com:mike/diaspora.git", + "visibility_level":0 + }, + "commits": [ + { + "id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", + "message": "Update Catalan translation to e38cb41.", + "timestamp": "2011-12-12T14:27:31+02:00", + "url": "http://example.com/mike/diaspora/commit/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", + "author": { + "name": "Jordi Mallach", + "email": "jordi@softcatala.org" + }, + "added": ["CHANGELOG"], + "modified": ["app/controller/application.rb"], + "removed": [] + }, + { + "id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", + "message": "fixed readme", + "timestamp": "2012-01-03T23:36:29+02:00", + "url": "http://example.com/mike/diaspora/commit/da1560886d4f094c3e6c9ef40349f7d38b5d27d7", + "author": { + "name": "GitLab dev user", + "email": "gitlabdev@dv6700.(none)" + }, + "added": ["CHANGELOG"], + "modified": ["app/controller/application.rb"], + "removed": [] + } + ], + "total_commits_count": 4 +} +` + + req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload))) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("X-Gitlab-Event", "Push Hook") + req.Header.Set("X-Gitlab-Token", "sampleToken!") + + Equal(t, err, nil) + + client := &http.Client{} + resp, err := client.Do(req) + Equal(t, err, nil) + + defer resp.Body.Close() + + Equal(t, resp.StatusCode, http.StatusOK) +} + +func TestTagEvent(t *testing.T) { + + payload := `{ + "object_kind": "tag_push", + "before": "0000000000000000000000000000000000000000", + "after": "82b3d5ae55f7080f1e6022629cdb57bfae7cccc7", + "ref": "refs/tags/v1.0.0", + "checkout_sha": "82b3d5ae55f7080f1e6022629cdb57bfae7cccc7", + "user_id": 1, + "user_name": "John Smith", + "user_avatar": "https://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=8://s.gravatar.com/avatar/d4c74594d841139328695756648b6bd6?s=80", + "project_id": 1, + "project":{ + "name":"Example", + "description":"", + "web_url":"http://example.com/jsmith/example", + "avatar_url":null, + "git_ssh_url":"git@example.com:jsmith/example.git", + "git_http_url":"http://example.com/jsmith/example.git", + "namespace":"Jsmith", + "visibility_level":0, + "path_with_namespace":"jsmith/example", + "default_branch":"master", + "homepage":"http://example.com/jsmith/example", + "url":"git@example.com:jsmith/example.git", + "ssh_url":"git@example.com:jsmith/example.git", + "http_url":"http://example.com/jsmith/example.git" + }, + "repository":{ + "name": "Example", + "url": "ssh://git@example.com/jsmith/example.git", + "description": "", + "homepage": "http://example.com/jsmith/example", + "git_http_url":"http://example.com/jsmith/example.git", + "git_ssh_url":"git@example.com:jsmith/example.git", + "visibility_level":0 + }, + "commits": [], + "total_commits_count": 0 +} +` + + req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload))) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("X-Gitlab-Event", "Tag Push Hook") + req.Header.Set("X-Gitlab-Token", "sampleToken!") + + Equal(t, err, nil) + + client := &http.Client{} + resp, err := client.Do(req) + Equal(t, err, nil) + + defer resp.Body.Close() + + Equal(t, resp.StatusCode, http.StatusOK) +} + +func TestIssueEvent(t *testing.T) { + + payload := `{ + "object_kind": "issue", + "user": { + "name": "Administrator", + "username": "root", + "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon" + }, + "project":{ + "name":"Gitlab Test", + "description":"Aut reprehenderit ut est.", + "web_url":"http://example.com/gitlabhq/gitlab-test", + "avatar_url":null, + "git_ssh_url":"git@example.com:gitlabhq/gitlab-test.git", + "git_http_url":"http://example.com/gitlabhq/gitlab-test.git", + "namespace":"GitlabHQ", + "visibility_level":20, + "path_with_namespace":"gitlabhq/gitlab-test", + "default_branch":"master", + "homepage":"http://example.com/gitlabhq/gitlab-test", + "url":"http://example.com/gitlabhq/gitlab-test.git", + "ssh_url":"git@example.com:gitlabhq/gitlab-test.git", + "http_url":"http://example.com/gitlabhq/gitlab-test.git" + }, + "repository":{ + "name": "Gitlab Test", + "url": "http://example.com/gitlabhq/gitlab-test.git", + "description": "Aut reprehenderit ut est.", + "homepage": "http://example.com/gitlabhq/gitlab-test" + }, + "object_attributes": { + "id": 301, + "title": "New API: create/update/delete file", + "assignee_id": 51, + "author_id": 51, + "project_id": 14, + "created_at": "2013-12-03T17:15:43Z", + "updated_at": "2013-12-03T17:15:43Z", + "position": 0, + "branch_name": null, + "description": "Create new API for manipulations with repository", + "milestone_id": null, + "state": "opened", + "iid": 23, + "url": "http://example.com/diaspora/issues/23", + "action": "open" + }, + "assignee": { + "name": "User1", + "username": "user1", + "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon" + } +} +` + + req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload))) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("X-Gitlab-Event", "Issue Hook") + req.Header.Set("X-Gitlab-Token", "sampleToken!") + + Equal(t, err, nil) + + client := &http.Client{} + resp, err := client.Do(req) + Equal(t, err, nil) + + defer resp.Body.Close() + + Equal(t, resp.StatusCode, http.StatusOK) +} + +func TestCommentCommitEvent(t *testing.T) { + + payload := `{ + "object_kind": "note", + "user": { + "name": "Administrator", + "username": "root", + "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon" + }, + "project_id": 5, + "project":{ + "name":"Gitlab Test", + "description":"Aut reprehenderit ut est.", + "web_url":"http://example.com/gitlabhq/gitlab-test", + "avatar_url":null, + "git_ssh_url":"git@example.com:gitlabhq/gitlab-test.git", + "git_http_url":"http://example.com/gitlabhq/gitlab-test.git", + "namespace":"GitlabHQ", + "visibility_level":20, + "path_with_namespace":"gitlabhq/gitlab-test", + "default_branch":"master", + "homepage":"http://example.com/gitlabhq/gitlab-test", + "url":"http://example.com/gitlabhq/gitlab-test.git", + "ssh_url":"git@example.com:gitlabhq/gitlab-test.git", + "http_url":"http://example.com/gitlabhq/gitlab-test.git" + }, + "repository":{ + "name": "Gitlab Test", + "url": "http://example.com/gitlab-org/gitlab-test.git", + "description": "Aut reprehenderit ut est.", + "homepage": "http://example.com/gitlab-org/gitlab-test" + }, + "object_attributes": { + "id": 1243, + "note": "This is a commit comment. How does this work?", + "noteable_type": "Commit", + "author_id": 1, + "created_at": "2015-05-17 18:08:09 UTC", + "updated_at": "2015-05-17 18:08:09 UTC", + "project_id": 5, + "attachment":null, + "line_code": "bec9703f7a456cd2b4ab5fb3220ae016e3e394e3_0_1", + "commit_id": "cfe32cf61b73a0d5e9f13e774abde7ff789b1660", + "noteable_id": null, + "system": false, + "st_diff": { + "diff": "--- /dev/null\n+++ b/six\n@@ -0,0 +1 @@\n+Subproject commit 409f37c4f05865e4fb208c771485f211a22c4c2d\n", + "new_path": "six", + "old_path": "six", + "a_mode": "0", + "b_mode": "160000", + "new_file": true, + "renamed_file": false, + "deleted_file": false + }, + "url": "http://example.com/gitlab-org/gitlab-test/commit/cfe32cf61b73a0d5e9f13e774abde7ff789b1660#note_1243" + }, + "commit": { + "id": "cfe32cf61b73a0d5e9f13e774abde7ff789b1660", + "message": "Add submodule\n\nSigned-off-by: Dmitriy Zaporozhets \u003cdmitriy.zaporozhets@gmail.com\u003e\n", + "timestamp": "2014-02-27T10:06:20+02:00", + "url": "http://example.com/gitlab-org/gitlab-test/commit/cfe32cf61b73a0d5e9f13e774abde7ff789b1660", + "author": { + "name": "Dmitriy Zaporozhets", + "email": "dmitriy.zaporozhets@gmail.com" + } + } +} +` + + req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload))) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("X-Gitlab-Event", "Note Hook") + req.Header.Set("X-Gitlab-Token", "sampleToken!") + + Equal(t, err, nil) + + client := &http.Client{} + resp, err := client.Do(req) + Equal(t, err, nil) + + defer resp.Body.Close() + + Equal(t, resp.StatusCode, http.StatusOK) +} + +func TestCommentMergeRequestEvent(t *testing.T) { + + payload := `{ + "object_kind": "note", + "user": { + "name": "Administrator", + "username": "root", + "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon" + }, + "project_id": 5, + "project":{ + "name":"Gitlab Test", + "description":"Aut reprehenderit ut est.", + "web_url":"http://example.com/gitlab-org/gitlab-test", + "avatar_url":null, + "git_ssh_url":"git@example.com:gitlab-org/gitlab-test.git", + "git_http_url":"http://example.com/gitlab-org/gitlab-test.git", + "namespace":"Gitlab Org", + "visibility_level":10, + "path_with_namespace":"gitlab-org/gitlab-test", + "default_branch":"master", + "homepage":"http://example.com/gitlab-org/gitlab-test", + "url":"http://example.com/gitlab-org/gitlab-test.git", + "ssh_url":"git@example.com:gitlab-org/gitlab-test.git", + "http_url":"http://example.com/gitlab-org/gitlab-test.git" + }, + "repository":{ + "name": "Gitlab Test", + "url": "http://localhost/gitlab-org/gitlab-test.git", + "description": "Aut reprehenderit ut est.", + "homepage": "http://example.com/gitlab-org/gitlab-test" + }, + "object_attributes": { + "id": 1244, + "note": "This MR needs work.", + "noteable_type": "MergeRequest", + "author_id": 1, + "created_at": "2015-05-17 18:21:36 UTC", + "updated_at": "2015-05-17 18:21:36 UTC", + "project_id": 5, + "attachment": null, + "line_code": null, + "commit_id": "", + "noteable_id": 7, + "system": false, + "st_diff": null, + "url": "http://example.com/gitlab-org/gitlab-test/merge_requests/1#note_1244" + }, + "merge_request": { + "id": 7, + "target_branch": "markdown", + "source_branch": "master", + "source_project_id": 5, + "author_id": 8, + "assignee_id": 28, + "title": "Tempora et eos debitis quae laborum et.", + "created_at": "2015-03-01 20:12:53 UTC", + "updated_at": "2015-03-21 18:27:27 UTC", + "milestone_id": 11, + "state": "opened", + "merge_status": "cannot_be_merged", + "target_project_id": 5, + "iid": 1, + "description": "Et voluptas corrupti assumenda temporibus. Architecto cum animi eveniet amet asperiores. Vitae numquam voluptate est natus sit et ad id.", + "position": 0, + "locked_at": null, + "source":{ + "name":"Gitlab Test", + "description":"Aut reprehenderit ut est.", + "web_url":"http://example.com/gitlab-org/gitlab-test", + "avatar_url":null, + "git_ssh_url":"git@example.com:gitlab-org/gitlab-test.git", + "git_http_url":"http://example.com/gitlab-org/gitlab-test.git", + "namespace":"Gitlab Org", + "visibility_level":10, + "path_with_namespace":"gitlab-org/gitlab-test", + "default_branch":"master", + "homepage":"http://example.com/gitlab-org/gitlab-test", + "url":"http://example.com/gitlab-org/gitlab-test.git", + "ssh_url":"git@example.com:gitlab-org/gitlab-test.git", + "http_url":"http://example.com/gitlab-org/gitlab-test.git" + }, + "target": { + "name":"Gitlab Test", + "description":"Aut reprehenderit ut est.", + "web_url":"http://example.com/gitlab-org/gitlab-test", + "avatar_url":null, + "git_ssh_url":"git@example.com:gitlab-org/gitlab-test.git", + "git_http_url":"http://example.com/gitlab-org/gitlab-test.git", + "namespace":"Gitlab Org", + "visibility_level":10, + "path_with_namespace":"gitlab-org/gitlab-test", + "default_branch":"master", + "homepage":"http://example.com/gitlab-org/gitlab-test", + "url":"http://example.com/gitlab-org/gitlab-test.git", + "ssh_url":"git@example.com:gitlab-org/gitlab-test.git", + "http_url":"http://example.com/gitlab-org/gitlab-test.git" + }, + "last_commit": { + "id": "562e173be03b8ff2efb05345d12df18815438a4b", + "message": "Merge branch 'another-branch' into 'master'\n\nCheck in this test\n", + "timestamp": "2015-04-08T21: 00:25-07:00", + "url": "http://example.com/gitlab-org/gitlab-test/commit/562e173be03b8ff2efb05345d12df18815438a4b", + "author": { + "name": "John Smith", + "email": "john@example.com" + } + }, + "work_in_progress": false, + "assignee": { + "name": "User1", + "username": "user1", + "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon" + } + } +} +` + + req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload))) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("X-Gitlab-Event", "Note Hook") + req.Header.Set("X-Gitlab-Token", "sampleToken!") + + Equal(t, err, nil) + + client := &http.Client{} + resp, err := client.Do(req) + Equal(t, err, nil) + + defer resp.Body.Close() + + Equal(t, resp.StatusCode, http.StatusOK) +} + +func TestCommentIssueEvent(t *testing.T) { + + payload := `{ + "object_kind": "note", + "user": { + "name": "Administrator", + "username": "root", + "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon" + }, + "project_id": 5, + "project":{ + "name":"Gitlab Test", + "description":"Aut reprehenderit ut est.", + "web_url":"http://example.com/gitlab-org/gitlab-test", + "avatar_url":null, + "git_ssh_url":"git@example.com:gitlab-org/gitlab-test.git", + "git_http_url":"http://example.com/gitlab-org/gitlab-test.git", + "namespace":"Gitlab Org", + "visibility_level":10, + "path_with_namespace":"gitlab-org/gitlab-test", + "default_branch":"master", + "homepage":"http://example.com/gitlab-org/gitlab-test", + "url":"http://example.com/gitlab-org/gitlab-test.git", + "ssh_url":"git@example.com:gitlab-org/gitlab-test.git", + "http_url":"http://example.com/gitlab-org/gitlab-test.git" + }, + "repository":{ + "name":"diaspora", + "url":"git@example.com:mike/diaspora.git", + "description":"", + "homepage":"http://example.com/mike/diaspora" + }, + "object_attributes": { + "id": 1241, + "note": "Hello world", + "noteable_type": "Issue", + "author_id": 1, + "created_at": "2015-05-17 17:06:40 UTC", + "updated_at": "2015-05-17 17:06:40 UTC", + "project_id": 5, + "attachment": null, + "line_code": null, + "commit_id": "", + "noteable_id": 92, + "system": false, + "st_diff": null, + "url": "http://example.com/gitlab-org/gitlab-test/issues/17#note_1241" + }, + "issue": { + "id": 92, + "title": "test", + "assignee_id": null, + "author_id": 1, + "project_id": 5, + "created_at": "2015-04-12 14:53:17 UTC", + "updated_at": "2015-04-26 08:28:42 UTC", + "position": 0, + "branch_name": null, + "description": "test", + "milestone_id": null, + "state": "closed", + "iid": 17 + } +} +` + + req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload))) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("X-Gitlab-Event", "Note Hook") + req.Header.Set("X-Gitlab-Token", "sampleToken!") + + Equal(t, err, nil) + + client := &http.Client{} + resp, err := client.Do(req) + Equal(t, err, nil) + + defer resp.Body.Close() + + Equal(t, resp.StatusCode, http.StatusOK) +} + +func TestCommentSunippetEvent(t *testing.T) { + + payload := `{ + "object_kind": "note", + "user": { + "name": "Administrator", + "username": "root", + "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon" + }, + "project_id": 5, + "project":{ + "name":"Gitlab Test", + "description":"Aut reprehenderit ut est.", + "web_url":"http://example.com/gitlab-org/gitlab-test", + "avatar_url":null, + "git_ssh_url":"git@example.com:gitlab-org/gitlab-test.git", + "git_http_url":"http://example.com/gitlab-org/gitlab-test.git", + "namespace":"Gitlab Org", + "visibility_level":10, + "path_with_namespace":"gitlab-org/gitlab-test", + "default_branch":"master", + "homepage":"http://example.com/gitlab-org/gitlab-test", + "url":"http://example.com/gitlab-org/gitlab-test.git", + "ssh_url":"git@example.com:gitlab-org/gitlab-test.git", + "http_url":"http://example.com/gitlab-org/gitlab-test.git" + }, + "repository":{ + "name":"Gitlab Test", + "url":"http://example.com/gitlab-org/gitlab-test.git", + "description":"Aut reprehenderit ut est.", + "homepage":"http://example.com/gitlab-org/gitlab-test" + }, + "object_attributes": { + "id": 1245, + "note": "Is this snippet doing what it's supposed to be doing?", + "noteable_type": "Snippet", + "author_id": 1, + "created_at": "2015-05-17 18:35:50 UTC", + "updated_at": "2015-05-17 18:35:50 UTC", + "project_id": 5, + "attachment": null, + "line_code": null, + "commit_id": "", + "noteable_id": 53, + "system": false, + "st_diff": null, + "url": "http://example.com/gitlab-org/gitlab-test/snippets/53#note_1245" + }, + "snippet": { + "id": 53, + "title": "test", + "content": "puts 'Hello world'", + "author_id": 1, + "project_id": 5, + "created_at": "2015-04-09 02:40:38 UTC", + "updated_at": "2015-04-09 02:40:38 UTC", + "file_name": "test.rb", + "expires_at": null, + "type": "ProjectSnippet", + "visibility_level": 0 + } +} +` + + req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload))) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("X-Gitlab-Event", "Note Hook") + req.Header.Set("X-Gitlab-Token", "sampleToken!") + + Equal(t, err, nil) + + client := &http.Client{} + resp, err := client.Do(req) + Equal(t, err, nil) + + defer resp.Body.Close() + + Equal(t, resp.StatusCode, http.StatusOK) +} + +func TestMergeRequestEvent(t *testing.T) { + + payload := `{ + "object_kind": "merge_request", + "user": { + "name": "Administrator", + "username": "root", + "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon" + }, + "object_attributes": { + "id": 99, + "target_branch": "master", + "source_branch": "ms-viewport", + "source_project_id": 14, + "author_id": 51, + "assignee_id": 6, + "title": "MS-Viewport", + "created_at": "2013-12-03T17:23:34Z", + "updated_at": "2013-12-03T17:23:34Z", + "st_commits": null, + "st_diffs": null, + "milestone_id": null, + "state": "opened", + "merge_status": "unchecked", + "target_project_id": 14, + "iid": 1, + "description": "", + "source":{ + "name":"Awesome Project", + "description":"Aut reprehenderit ut est.", + "web_url":"http://example.com/awesome_space/awesome_project", + "avatar_url":null, + "git_ssh_url":"git@example.com:awesome_space/awesome_project.git", + "git_http_url":"http://example.com/awesome_space/awesome_project.git", + "namespace":"Awesome Space", + "visibility_level":20, + "path_with_namespace":"awesome_space/awesome_project", + "default_branch":"master", + "homepage":"http://example.com/awesome_space/awesome_project", + "url":"http://example.com/awesome_space/awesome_project.git", + "ssh_url":"git@example.com:awesome_space/awesome_project.git", + "http_url":"http://example.com/awesome_space/awesome_project.git" + }, + "target": { + "name":"Awesome Project", + "description":"Aut reprehenderit ut est.", + "web_url":"http://example.com/awesome_space/awesome_project", + "avatar_url":null, + "git_ssh_url":"git@example.com:awesome_space/awesome_project.git", + "git_http_url":"http://example.com/awesome_space/awesome_project.git", + "namespace":"Awesome Space", + "visibility_level":20, + "path_with_namespace":"awesome_space/awesome_project", + "default_branch":"master", + "homepage":"http://example.com/awesome_space/awesome_project", + "url":"http://example.com/awesome_space/awesome_project.git", + "ssh_url":"git@example.com:awesome_space/awesome_project.git", + "http_url":"http://example.com/awesome_space/awesome_project.git" + }, + "last_commit": { + "id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", + "message": "fixed readme", + "timestamp": "2012-01-03T23:36:29+02:00", + "url": "http://example.com/awesome_space/awesome_project/commits/da1560886d4f094c3e6c9ef40349f7d38b5d27d7", + "author": { + "name": "GitLab dev user", + "email": "gitlabdev@dv6700.(none)" + } + }, + "work_in_progress": false, + "url": "http://example.com/diaspora/merge_requests/1", + "action": "open", + "assignee": { + "name": "User1", + "username": "user1", + "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon" + } + } +} +` + + req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload))) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("X-Gitlab-Event", "Merge Request Hook") + req.Header.Set("X-Gitlab-Token", "sampleToken!") + + Equal(t, err, nil) + + client := &http.Client{} + resp, err := client.Do(req) + Equal(t, err, nil) + + defer resp.Body.Close() + + Equal(t, resp.StatusCode, http.StatusOK) +} + +func TestWikipageEvent(t *testing.T) { + + payload := `{ + "object_kind": "wiki_page", + "user": { + "name": "Administrator", + "username": "root", + "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon" + }, + "project": { + "name": "awesome-project", + "description": "This is awesome", + "web_url": "http://example.com/root/awesome-project", + "avatar_url": null, + "git_ssh_url": "git@example.com:root/awesome-project.git", + "git_http_url": "http://example.com/root/awesome-project.git", + "namespace": "root", + "visibility_level": 0, + "path_with_namespace": "root/awesome-project", + "default_branch": "master", + "homepage": "http://example.com/root/awesome-project", + "url": "git@example.com:root/awesome-project.git", + "ssh_url": "git@example.com:root/awesome-project.git", + "http_url": "http://example.com/root/awesome-project.git" + }, + "wiki": { + "web_url": "http://example.com/root/awesome-project/wikis/home", + "git_ssh_url": "git@example.com:root/awesome-project.wiki.git", + "git_http_url": "http://example.com/root/awesome-project.wiki.git", + "path_with_namespace": "root/awesome-project.wiki", + "default_branch": "master" + }, + "object_attributes": { + "title": "Awesome", + "content": "awesome content goes here", + "format": "markdown", + "message": "adding an awesome page to the wiki", + "slug": "awesome", + "url": "http://example.com/root/awesome-project/wikis/awesome", + "action": "create" + } +} +` + + req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload))) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("X-Gitlab-Event", "Wiki Page Hook") + req.Header.Set("X-Gitlab-Token", "sampleToken!") + + Equal(t, err, nil) + + client := &http.Client{} + resp, err := client.Do(req) + Equal(t, err, nil) + + defer resp.Body.Close() + + Equal(t, resp.StatusCode, http.StatusOK) +} + +func TestPipelineEvent(t *testing.T) { + + payload := `{ + "object_kind": "pipeline", + "object_attributes":{ + "id": 31, + "ref": "master", + "tag": false, + "sha": "bcbb5ec396a2c0f828686f14fac9b80b780504f2", + "before_sha": "bcbb5ec396a2c0f828686f14fac9b80b780504f2", + "status": "success", + "stages":[ + "build", + "test", + "deploy" + ], + "created_at": "2016-08-12 15:23:28 UTC", + "finished_at": "2016-08-12 15:26:29 UTC", + "duration": 63 + }, + "user":{ + "name": "Administrator", + "username": "root", + "avatar_url": "http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon" + }, + "project":{ + "name": "Gitlab Test", + "description": "Atque in sunt eos similique dolores voluptatem.", + "web_url": "http://192.168.64.1:3005/gitlab-org/gitlab-test", + "avatar_url": null, + "git_ssh_url": "git@192.168.64.1:gitlab-org/gitlab-test.git", + "git_http_url": "http://192.168.64.1:3005/gitlab-org/gitlab-test.git", + "namespace": "Gitlab Org", + "visibility_level": 20, + "path_with_namespace": "gitlab-org/gitlab-test", + "default_branch": "master" + }, + "commit":{ + "id": "bcbb5ec396a2c0f828686f14fac9b80b780504f2", + "message": "test\n", + "timestamp": "2016-08-12T17:23:21+02:00", + "url": "http://example.com/gitlab-org/gitlab-test/commit/bcbb5ec396a2c0f828686f14fac9b80b780504f2", + "author":{ + "name": "User", + "email": "user@gitlab.com" + } + }, + "builds":[ + { + "id": 380, + "stage": "deploy", + "name": "production", + "status": "skipped", + "created_at": "2016-08-12 15:23:28 UTC", + "started_at": null, + "finished_at": null, + "when": "manual", + "manual": true, + "user":{ + "name": "Administrator", + "username": "root", + "avatar_url": "http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon" + }, + "runner": null, + "artifacts_file":{ + "filename": null, + "size": null + } + }, + { + "id": 377, + "stage": "test", + "name": "test-image", + "status": "success", + "created_at": "2016-08-12 15:23:28 UTC", + "started_at": "2016-08-12 15:26:12 UTC", + "finished_at": null, + "when": "on_success", + "manual": false, + "user":{ + "name": "Administrator", + "username": "root", + "avatar_url": "http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon" + }, + "runner": null, + "artifacts_file":{ + "filename": null, + "size": null + } + }, + { + "id": 378, + "stage": "test", + "name": "test-build", + "status": "success", + "created_at": "2016-08-12 15:23:28 UTC", + "started_at": "2016-08-12 15:26:12 UTC", + "finished_at": "2016-08-12 15:26:29 UTC", + "when": "on_success", + "manual": false, + "user":{ + "name": "Administrator", + "username": "root", + "avatar_url": "http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon" + }, + "runner": null, + "artifacts_file":{ + "filename": null, + "size": null + } + }, + { + "id": 376, + "stage": "build", + "name": "build-image", + "status": "success", + "created_at": "2016-08-12 15:23:28 UTC", + "started_at": "2016-08-12 15:24:56 UTC", + "finished_at": "2016-08-12 15:25:26 UTC", + "when": "on_success", + "manual": false, + "user":{ + "name": "Administrator", + "username": "root", + "avatar_url": "http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon" + }, + "runner": null, + "artifacts_file":{ + "filename": null, + "size": null + } + }, + { + "id": 379, + "stage": "deploy", + "name": "staging", + "status": "created", + "created_at": "2016-08-12 15:23:28 UTC", + "started_at": null, + "finished_at": null, + "when": "on_success", + "manual": false, + "user":{ + "name": "Administrator", + "username": "root", + "avatar_url": "http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon" + }, + "runner": null, + "artifacts_file":{ + "filename": null, + "size": null + } + } + ] +} +` + + req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload))) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("X-Gitlab-Event", "Pipeline Hook") + req.Header.Set("X-Gitlab-Token", "sampleToken!") + + Equal(t, err, nil) + + client := &http.Client{} + resp, err := client.Do(req) + Equal(t, err, nil) + + defer resp.Body.Close() + + Equal(t, resp.StatusCode, http.StatusOK) +} + +func TestBuildEvent(t *testing.T) { + + payload := `{ + "object_kind": "build", + "ref": "gitlab-script-trigger", + "tag": false, + "before_sha": "2293ada6b400935a1378653304eaf6221e0fdb8f", + "sha": "2293ada6b400935a1378653304eaf6221e0fdb8f", + "build_id": 1977, + "build_name": "test", + "build_stage": "test", + "build_status": "created", + "build_started_at": null, + "build_finished_at": null, + "build_duration": null, + "build_allow_failure": false, + "project_id": 380, + "project_name": "gitlab-org/gitlab-test", + "user": { + "id": 3, + "name": "User", + "email": "user@gitlab.com" + }, + "commit": { + "id": 2366, + "sha": "2293ada6b400935a1378653304eaf6221e0fdb8f", + "message": "test\n", + "author_name": "User", + "author_email": "user@gitlab.com", + "status": "created", + "duration": null, + "started_at": null, + "finished_at": null + }, + "repository": { + "name": "gitlab_test", + "git_ssh_url": "git@192.168.64.1:gitlab-org/gitlab-test.git", + "description": "Atque in sunt eos similique dolores voluptatem.", + "homepage": "http://192.168.64.1:3005/gitlab-org/gitlab-test", + "git_ssh_url": "git@192.168.64.1:gitlab-org/gitlab-test.git", + "git_http_url": "http://192.168.64.1:3005/gitlab-org/gitlab-test.git", + "visibility_level": 20 + } +} +` + + req, err := http.NewRequest("POST", "http://127.0.0.1:3009/webhooks", bytes.NewBuffer([]byte(payload))) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("X-Gitlab-Event", "Build Hook") + req.Header.Set("X-Gitlab-Token", "sampleToken!") + + Equal(t, err, nil) + + client := &http.Client{} + resp, err := client.Do(req) + Equal(t, err, nil) + + defer resp.Body.Close() + + Equal(t, resp.StatusCode, http.StatusOK) +} diff --git a/gitlab/payload.go b/gitlab/payload.go new file mode 100644 index 0000000..02a40bf --- /dev/null +++ b/gitlab/payload.go @@ -0,0 +1,404 @@ +package gitlab + +import ( + "strings" + "time" +) + +type customTime struct { + time.Time +} + +func (t *customTime) UnmarshalJSON(b []byte) (err error) { + layout := []string{ + "2006-01-02 15:04:05 MST", + "2006-01-02 15:04:05 Z07:00", + "2006-01-02 15:04:05 Z0700", + time.RFC3339, + } + s := strings.Trim(string(b), "\"") + if s == "null" { + t.Time = time.Time{} + return + } + for _, l := range layout { + t.Time, err = time.Parse(l, s) + if err == nil { + break + } + } + return +} + +// IssueEventPayload contains the information for GitLab's issue event +type IssueEventPayload struct { + ObjectKind string `json:"object_kind"` + User User `json:"user"` + Project Project `json:"project"` + Repository Repository `json:"repository"` + ObjectAttributes ObjectAttributes `json:"object_attributes"` + Assignee Assignee `json:"assignee"` +} + +// MergeRequestEventPayload contains the information for GitLab's merge request event +type MergeRequestEventPayload struct { + ObjectKind string `json:"object_kind"` + User User `json:"user"` + ObjectAttributes ObjectAttributes `json:"object_attributes"` +} + +// PushEventPayload contains the information for GitLab's push event +type PushEventPayload struct { + ObjectKind string `json:"object_kind"` + Before string `json:"before"` + After string `json:"after"` + Ref string `json:"ref"` + CheckoutSHA string `json:"checkout_sha"` + UserID int `json:"user_id"` + UserName string `json:"user_name"` + UserEmail string `json:"user_email"` + UserAvatar string `json:"user_avatar"` + ProjectID int `json:"project_id"` + Project Project `json:"Project"` + Repository Repository `json:"repository"` + Commits []Commit `json:"commits"` + TotalCommitsCount int `json:"total_commits_count"` +} + +// TagEventPayload contains the information for GitLab's tag push event +type TagEventPayload struct { + ObjectKind string `json:"object_kind"` + Before string `json:"before"` + After string `json:"after"` + Ref string `json:"ref"` + CheckoutSHA string `json:"checkout_sha"` + UserID int `json:"user_id"` + UserName string `json:"user_name"` + UserAvatar string `json:"user_avatar"` + ProjectID int `json:"project_id"` + Project Project `json:"Project"` + Repository Repository `json:"repository"` + Commits []Commit `json:"commits"` + TotalCommitsCount int `json:"total_commits_count"` +} + +// WikiPageEventPayload contains the information for GitLab's wiki created/updated event +type WikiPageEventPayload struct { + ObjectKind string `json:"object_kind"` + User User `json:"user"` + Project Project `json:"project"` + Wiki Wiki `json:"wiki"` + ObjectAttributes ObjectAttributes `json:"object_attributes"` +} + +// PipelineEventPayload contains the information for GitLab's pipeline status change event +type PipelineEventPayload struct { + ObjectKind string `json:"object_kind"` + User User `json:"user"` + Project Project `json:"project"` + Commit Commit `json:"commit"` + ObjectAttributes ObjectAttributes `json:"object_attributes"` + Builds []Build `json:"builds"` +} + +// CommentEventPayload contains the information for GitLab's comment event +type CommentEventPayload struct { + ObjectKind string `json:"object_kind"` + User User `json:"user"` + ProjectID int `json:"project_id"` + Project Project `json:"project"` + Repository Repository `json:"repository"` + ObjectAttributes ObjectAttributes `json:"object_attributes"` + MergeRequest MergeRequest `json:"merge_request"` + Commit Commit `json:"commit"` + Issue Issue `json:"issue"` + Snippet Snippet `json:"snippet"` +} + +// BuildEventPayload contains the information for GitLab's build status change event +type BuildEventPayload struct { + ObjectKind string `json:"object_kind"` + Ref string `json:"ref"` + Tag bool `json:"tag"` + BeforeSHA string `json:"before_sha"` + SHA string `json:"sha"` + BuildID int `json:"build_id"` + BuildName string `json:"build_name"` + BuildStage string `json:"build_stage"` + BuildStatus string `json:"build_status"` + BuildStartedAt customTime `json:"build_started_at"` + BuildFinishedAt customTime `json:"build_finished_at"` + BuildDuration int `json:"build_duration"` + BuildAllowFailure bool `json:"build_allow_failure"` + ProjectID int `json:"project_id"` + ProjectName string `json:"project_name"` + User User `json:"user"` + Commit BuildCommit `json:"commit"` + Repository Repository `json:"repository"` +} + +// Issue contais all of the GitLab issue information +type Issue struct { + ID int `json:"id"` + Title string `json:"title"` + AssigneeID int `json:"assignee_id"` + AuthorID int `json:"author_id"` + ProjectID int `json:"project_id"` + CreatedAt customTime `json:"created_at"` + UpdatedAt customTime `json:"updated_at"` + Position int `json:"position"` + BranchName string `json:"branch_name"` + Description string `json:"description"` + MilestoneID int `json:"milestone_id"` + State string `json:"state"` + IID int `json:"iid"` +} + +// Build contais all of the GitLab build information +type Build struct { + ID int `json:"id"` + Stage string `json:"stage"` + Name string `json:"name"` + Status string `json:"status"` + CreatedAt customTime `json:"created_at"` + StartedAt customTime `json:"started_at"` + FinishedAt customTime `json:"finished_at"` + When string `json:"when"` + Manual bool `json:"manual"` + User User `json:"user"` + Runner string `json:"runner"` + ArtifactsFile ArtifactsFile `json:"artifactsfile"` +} + +// ArtifactsFile contais all of the GitLab artifact information +type ArtifactsFile struct { + Filename string `json:"filename"` + Size string `json:"size"` +} + +// Wiki contais all of the GitLab wiki information +type Wiki struct { + WebURL string `json:"web_url"` + GitSSHURL string `json:"git_ssh_url"` + GitHTTPURL string `json:"git_http_url"` + PathWithNamespace string `json:"path_with_namespace"` + DefaultBranch string `json:"default_branch"` +} + +// Commit contais all of the GitLab commit information +type Commit struct { + ID string `json:"id"` + Message string `json:"message"` + Timestamp customTime `json:"timestamp"` + URL string `json:"url"` + Author Author `json:"author"` + Added []string `json:"added"` + Modified []string `json:"modified"` + Removed []string `json:"removed"` +} + +// BuildCommit contais all of the GitLab build commit information +type BuildCommit struct { + ID int `json:"id"` + SHA string `json:"sha"` + Message string `json:"message"` + AuthorName string `json:"auuthor_name"` + AuthorEmail string `json:"author_email"` + Status string `json:"status"` + Duration int `json:"duration"` + StartedAt customTime `json:"started_at"` + FinishedAt customTime `json:"finished_at"` +} + +// Snippet contais all of the GitLab snippet information +type Snippet struct { + ID int `json:"id"` + Title string `json:"title"` + Content string `json:"content"` + AuthorID int `json:"author_id"` + ProjectID int `json:"project_id"` + CreatedAt customTime `json:"created_at"` + UpdatedAt customTime `json:"updated_at"` + FileName string `json:"file_name"` + ExpiresAt customTime `json:"expires_at"` + Type string `json:"type"` + VisibilityLevel int `json:"visibility_level"` +} + +// User contais all of the GitLab user information +type User struct { + Name string `json:"name"` + UserName string `json:"username"` + AvatarURL string `json:"avatar_url"` +} + +// Project contais all of the GitLab project information +type Project struct { + Name string `json:"name"` + Description string `json:"description"` + WebURL string `json:"web_url"` + AvatarURL string `json:"avatar_url"` + GitSSSHURL string `json:"git_ssh_url"` + GitHTTPURL string `json:"git_http_url"` + Namespace string `json:"namespace"` + VisibilityLevel int `json:"visibility_level"` + PathWithNamespace string `json:"path_with_namespace"` + DefaultBranch string `json:"default_branch"` + Homepage string `json:"homepage"` + URL string `json:"url"` + SSHURL string `json:"ssh_url"` + HTTPURL string `json:"http_url"` +} + +// Repository contais all of the GitLab repository information +type Repository struct { + Name string `json:"name"` + URL string `json:"url"` + Description string `json:"description"` + Homepage string `json:"homepage"` +} + +// ObjectAttributes contais all of the GitLab object attributes information +type ObjectAttributes struct { + ID int `json:"id"` + Title string `json:"title"` + AssigneeID int `json:"assignee_id"` + AuthorID int `json:"author_id"` + ProjectID int `json:"project_id"` + CreatedAt customTime `json:"created_at"` + UpdatedAt customTime `json:"updated_at"` + Position int `json:"position"` + BranchName string `json:"branch_name"` + Description string `json:"description"` + MilestoneID int `json:"milestone_id"` + State string `json:"state"` + IID int `json:"iid"` + URL string `json:"url"` + Action string `json:"action"` + TargetBranch string `json:"target_branch"` + SourceBranch string `json:"source_branch"` + SourceProjectID int `json:"source_project_id"` + TargetProjectID int `json:"target_project_id"` + StCommits string `json:"st_commits"` + MergeStatus string `json:"merge_status"` + Content string `json:"content"` + Format string `json:"format"` + Message string `json:"message"` + Slug string `json:"slug"` + Ref string `json:"ref"` + Tag bool `json:"tag"` + SHA string `json:"sha"` + BeforeSHA string `json:"before_sha"` + Status string `json:"status"` + Stages []string `json:"stages"` + Duration int `json:"duration"` + Note string `json:"note"` + NotebookType string `json:"noteable_type"` + At customTime `json:"attachment"` + LineCode string `json:"line_code"` + CommitID string `json:"commit_id"` + NoteableID int `json:"noteable_id"` + System bool `json:"system"` + WorkInProgress bool `json:"work_in_progress"` + StDiffs []StDiff `json:"st_diffs"` + Source Source `json:"source"` + Target Target `json:"target"` + LastCommit LastCommit `json:"last_commit"` + Assignee Assignee `json:"assignee"` +} + +// MergeRequest contais all of the GitLab merge request information +type MergeRequest struct { + ID int `json:"id"` + TargetBranch string `json:"target_branch"` + SourceBranch string `json:"source_branch"` + SourceProjectID string `json:"source_project_id"` + AssigneeID int `json:"assignee_id"` + AuthorID int `json:"author_id"` + Title string `json:"title"` + CreatedAt customTime `json:"created_at"` + UpdatedAt customTime `json:"updated_at"` + MilestoneID int `json:"milestone_id"` + State string `json:"state"` + MergeStatus string `json:"merge_status"` + TargetProjectID int `json:"target_project_id"` + IID int `json:"iid"` + Description string `json:"description"` + Position int `json:"position"` + LockedAt customTime `json:"locked_at"` + Source Source `json:"source"` + Target Target `json:"target"` + LastCommit LastCommit `json:"last_commit"` + WorkInProgress bool `json:"work_in_progress"` + Assignee Assignee `json:"assignee"` +} + +// Assignee contais all of the GitLab assignee information +type Assignee struct { + Name string `json:"name"` + Username string `json:"username"` + AvatarURL string `json:"avatar_url"` +} + +// StDiff contais all of the GitLab diff information +type StDiff struct { + Diff string `json:"diff"` + NewPath string `json:"new_path"` + OldPath string `json:"old_path"` + AMode string `json:"a_mode"` + BMode string `json:"b_mode"` + NewFile bool `json:"new_file"` + RenamedFile bool `json:"renamed_file"` + DeletedFile bool `json:"deleted_file"` +} + +// Source contais all of the GitLab source information +type Source struct { + Name string `json:"name"` + Description string `json:"description"` + WebURL string `json:"web_url"` + AvatarURL string `json:"avatar_url"` + GitSSHURL string `json:"git_ssh_url"` + GitHTTPURL string `json:"git_http_url"` + Namespace string `json:"namespace"` + VisibilityLevel int `json:"visibility_level"` + PathWithNamespace string `json:"path_with_namespace"` + DefaultBranch string `json:"default_branch"` + Homepage string `json:"homepage"` + URL string `json:"url"` + SSHURL string `json:"ssh_url"` + HTTPURL string `json:"http_url"` +} + +// Target contais all of the GitLab target information +type Target struct { + Name string `json:"name"` + Description string `json:"description"` + WebURL string `json:"web_url"` + AvatarURL string `json:"avatar_url"` + GitSSHURL string `json:"git_ssh_url"` + GitHTTPURL string `json:"git_http_url"` + Namespace string `json:"namespace"` + VisibilityLevel int `json:"visibility_level"` + PathWithNamespace string `json:"path_with_namespace"` + DefaultBranch string `json:"default_branch"` + Homepage string `json:"homepage"` + URL string `json:"url"` + SSHURL string `json:"ssh_url"` + HTTPURL string `json:"http_url"` +} + +// LastCommit contais all of the GitLab last commit information +type LastCommit struct { + ID string `json:"id"` + Message string `json:"message"` + Timestamp customTime `json:"timestamp"` + URL string `json:"url"` + Author Author `json:"author"` +} + +// Author contais all of the GitLab author information +type Author struct { + Name string `json:"name"` + Email string `json:"email"` +} diff --git a/webhooks.go b/webhooks.go index fcd1ba2..1e61809 100644 --- a/webhooks.go +++ b/webhooks.go @@ -2,7 +2,7 @@ package webhooks import "net/http" -// Webhook provides http.Header to minimize imports +// Header provides http.Header to minimize imports type Header http.Header // Provider defines the type of webhook @@ -14,6 +14,8 @@ func (p Provider) String() string { return "GitHub" case Bitbucket: return "Bitbucket" + case GitLab: + return "GitLab" default: return "Unknown" } @@ -23,6 +25,7 @@ func (p Provider) String() string { const ( GitHub Provider = iota Bitbucket + GitLab ) // Webhook interface defines a webhook to recieve events @@ -67,7 +70,7 @@ func RunServer(s *http.Server, hook Webhook, path string) error { // RunTLSServer runs a custom server with TLS configuration. // NOTE: http.Server Handler will be overridden by this library, just set it to nil. // Setting the Certificates can be done in the http.Server.TLSConfig.Certificates -// see example here: https://gopkg.in/go-playground/webhooks.v1/blob/master/webhooks_test.go#L178 +// see example here: https://github.com/go-playground/webhooks/blob/v2/webhooks_test.go#L178 func RunTLSServer(s *http.Server, hook Webhook, path string) error { srv := &server{ diff --git a/webhooks_test.go b/webhooks_test.go index 9ade530..1bec132 100644 --- a/webhooks_test.go +++ b/webhooks_test.go @@ -236,5 +236,6 @@ func TestProviderString(t *testing.T) { Equal(t, GitHub.String(), "GitHub") Equal(t, Bitbucket.String(), "Bitbucket") + Equal(t, GitLab.String(), "GitLab") Equal(t, Provider(999999).String(), "Unknown") }