From ddee4448143fef4d55d9b3946fb7c764248f843e Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Mon, 16 Sep 2024 11:48:58 -0400 Subject: [PATCH] refactor(hook): nest API response and use Gorm preload to populate repo and build data (#1147) * init commit * fix tests, prune duplicate data, prettify * gci * use int64 for DB reps of integer fields --------- Co-authored-by: David May <49894298+wass3rw3rk@users.noreply.github.com> --- api/admin/hook.go | 4 +- api/hook/create.go | 6 +- api/hook/redeliver.go | 2 +- api/hook/update.go | 4 +- api/repo/create.go | 5 +- api/repo/repair.go | 2 +- api/types/hook.go | 426 +++++++++++++++++++++++++ api/types/hook_test.go | 249 +++++++++++++++ api/webhook/post.go | 18 +- constants/driver.go | 64 ++++ constants/repo.go | 36 +++ constants/table.go | 39 +++ database/hook/count.go | 2 +- database/hook/count_repo.go | 2 +- database/hook/count_repo_test.go | 52 ++- database/hook/count_test.go | 26 +- database/hook/create.go | 32 +- database/hook/create_test.go | 10 +- database/hook/delete.go | 13 +- database/hook/delete_test.go | 18 +- database/hook/get.go | 23 +- database/hook/get_repo.go | 22 +- database/hook/get_repo_test.go | 74 ++++- database/hook/get_test.go | 61 +++- database/hook/get_webhook.go | 23 +- database/hook/get_webhook_test.go | 55 +++- database/hook/hook.go | 4 +- database/hook/hook_test.go | 51 +++ database/hook/interface.go | 19 +- database/hook/last_repo.go | 17 +- database/hook/last_repo_test.go | 60 +++- database/hook/list.go | 25 +- database/hook/list_repo.go | 24 +- database/hook/list_repo_test.go | 97 ++++-- database/hook/list_test.go | 90 ++++-- database/hook/opts.go | 10 + database/hook/table.go | 2 +- database/hook/update.go | 29 +- database/hook/update_test.go | 18 +- database/integration_test.go | 102 +++++- database/resource.go | 1 + database/testutils/api_resources.go | 8 +- database/types/hook.go | 228 +++++++++++++ database/types/hook_test.go | 227 +++++++++++++ internal/webhook.go | 2 +- mock/server/hook.go | 258 +++++++++++++-- mock/server/hook_test.go | 4 +- router/middleware/hook/context.go | 14 +- router/middleware/hook/context_test.go | 6 +- router/middleware/hook/hook.go | 4 +- router/middleware/hook/hook_test.go | 40 ++- scm/github/repo.go | 4 +- scm/github/repo_test.go | 4 +- scm/github/webhook.go | 39 ++- scm/github/webhook_test.go | 72 ++--- scm/service.go | 4 +- 56 files changed, 2346 insertions(+), 385 deletions(-) create mode 100644 api/types/hook.go create mode 100644 api/types/hook_test.go create mode 100644 constants/driver.go create mode 100644 constants/repo.go create mode 100644 database/types/hook.go create mode 100644 database/types/hook_test.go diff --git a/api/admin/hook.go b/api/admin/hook.go index a7d0ef303..5b65501e1 100644 --- a/api/admin/hook.go +++ b/api/admin/hook.go @@ -9,9 +9,9 @@ import ( "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" + "github.com/go-vela/server/api/types" "github.com/go-vela/server/database" "github.com/go-vela/server/util" - "github.com/go-vela/types/library" ) // swagger:operation PUT /api/v1/admin/hook admin AdminUpdateHook @@ -57,7 +57,7 @@ func UpdateHook(c *gin.Context) { l.Debug("platform admin: updating hook") // capture body from API request - input := new(library.Hook) + input := new(types.Hook) err := c.Bind(input) if err != nil { diff --git a/api/hook/create.go b/api/hook/create.go index 67eb86789..67778be39 100644 --- a/api/hook/create.go +++ b/api/hook/create.go @@ -10,10 +10,10 @@ import ( "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" + "github.com/go-vela/server/api/types" "github.com/go-vela/server/database" "github.com/go-vela/server/router/middleware/repo" "github.com/go-vela/server/util" - "github.com/go-vela/types/library" ) // swagger:operation POST /api/v1/hooks/{org}/{repo} webhook CreateHook @@ -74,7 +74,7 @@ func CreateHook(c *gin.Context) { l.Debugf("creating new hook for repo %s", r.GetFullName()) // capture body from API request - input := new(library.Hook) + input := new(types.Hook) err := c.Bind(input) if err != nil { @@ -96,7 +96,7 @@ func CreateHook(c *gin.Context) { } // update fields in webhook object - input.SetRepoID(r.GetID()) + input.SetRepo(r) input.SetNumber(1) if input.GetCreated() == 0 { diff --git a/api/hook/redeliver.go b/api/hook/redeliver.go index 13ec3f57a..91aa3a1ec 100644 --- a/api/hook/redeliver.go +++ b/api/hook/redeliver.go @@ -76,7 +76,7 @@ func RedeliverHook(c *gin.Context) { l.Debugf("redelivering hook %s", entry) - err := scm.FromContext(c).RedeliverWebhook(c, u, r, h) + err := scm.FromContext(c).RedeliverWebhook(c, u, h) if err != nil { retErr := fmt.Errorf("unable to redeliver hook %s: %w", entry, err) diff --git a/api/hook/update.go b/api/hook/update.go index 75492e45a..7017ae6fc 100644 --- a/api/hook/update.go +++ b/api/hook/update.go @@ -9,11 +9,11 @@ import ( "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" + "github.com/go-vela/server/api/types" "github.com/go-vela/server/database" "github.com/go-vela/server/router/middleware/hook" "github.com/go-vela/server/router/middleware/repo" "github.com/go-vela/server/util" - "github.com/go-vela/types/library" ) // swagger:operation PUT /api/v1/hooks/{org}/{repo}/{hook} webhook UpdateHook @@ -82,7 +82,7 @@ func UpdateHook(c *gin.Context) { l.Debugf("updating hook %s", entry) // capture body from API request - input := new(library.Hook) + input := new(types.Hook) err := c.Bind(input) if err != nil { diff --git a/api/repo/create.go b/api/repo/create.go index 25719f2b4..cdd6848a3 100644 --- a/api/repo/create.go +++ b/api/repo/create.go @@ -20,7 +20,6 @@ import ( "github.com/go-vela/server/scm" "github.com/go-vela/server/util" "github.com/go-vela/types/constants" - "github.com/go-vela/types/library" ) // swagger:operation POST /api/v1/repos repos CreateRepo @@ -241,7 +240,7 @@ func CreateRepo(c *gin.Context) { r.SetHash(dbRepo.GetHash()) } - h := new(library.Hook) + h := new(types.Hook) // err being nil means we have a record of this repo (dbRepo) if err == nil { @@ -321,7 +320,7 @@ func CreateRepo(c *gin.Context) { // create init hook in the DB after repo has been added in order to capture its ID if c.Value("webhookvalidation").(bool) { // update initialization hook - h.SetRepoID(r.GetID()) + h.SetRepo(r) // create first hook for repo in the database _, err = database.FromContext(c).CreateHook(ctx, h) if err != nil { diff --git a/api/repo/repair.go b/api/repo/repair.go index 27e720c37..2fd99f9e2 100644 --- a/api/repo/repair.go +++ b/api/repo/repair.go @@ -112,7 +112,7 @@ func RepairRepo(c *gin.Context) { return } - hook.SetRepoID(r.GetID()) + hook.SetRepo(r) _, err = database.FromContext(c).CreateHook(ctx, hook) if err != nil { diff --git a/api/types/hook.go b/api/types/hook.go new file mode 100644 index 000000000..18549e042 --- /dev/null +++ b/api/types/hook.go @@ -0,0 +1,426 @@ +// SPDX-License-Identifier: Apache-2.0 + +package types + +import ( + "fmt" +) + +// Hook is the API representation of a webhook. +// +// swagger:model Webhook +type Hook struct { + ID *int64 `json:"id,omitempty"` + Repo *Repo `json:"repo,omitempty"` + Build *Build `json:"build,omitempty"` + Number *int `json:"number,omitempty"` + SourceID *string `json:"source_id,omitempty"` + Created *int64 `json:"created,omitempty"` + Host *string `json:"host,omitempty"` + Event *string `json:"event,omitempty"` + EventAction *string `json:"event_action,omitempty"` + Branch *string `json:"branch,omitempty"` + Error *string `json:"error,omitempty"` + Status *string `json:"status,omitempty"` + Link *string `json:"link,omitempty"` + WebhookID *int64 `json:"webhook_id,omitempty"` +} + +// GetID returns the ID field. +// +// When the provided Hook type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (h *Hook) GetID() int64 { + // return zero value if Hook type or ID field is nil + if h == nil || h.ID == nil { + return 0 + } + + return *h.ID +} + +// GetRepo returns the Repo field. +// +// When the provided Hook type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (h *Hook) GetRepo() *Repo { + // return zero value if Hook type or Repo field is nil + if h == nil || h.Repo == nil { + return new(Repo) + } + + return h.Repo +} + +// GetBuild returns the Build field. +// +// When the provided Hook type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (h *Hook) GetBuild() *Build { + // return zero value if Hook type or Build field is nil + if h == nil || h.Build == nil { + return new(Build) + } + + return h.Build +} + +// GetNumber returns the Number field. +// +// When the provided Hook type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (h *Hook) GetNumber() int { + // return zero value if Hook type or BuildID field is nil + if h == nil || h.Number == nil { + return 0 + } + + return *h.Number +} + +// GetSourceID returns the SourceID field. +// +// When the provided Hook type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (h *Hook) GetSourceID() string { + // return zero value if Hook type or SourceID field is nil + if h == nil || h.SourceID == nil { + return "" + } + + return *h.SourceID +} + +// GetCreated returns the Created field. +// +// When the provided Hook type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (h *Hook) GetCreated() int64 { + // return zero value if Hook type or Created field is nil + if h == nil || h.Created == nil { + return 0 + } + + return *h.Created +} + +// GetHost returns the Host field. +// +// When the provided Hook type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (h *Hook) GetHost() string { + // return zero value if Hook type or Host field is nil + if h == nil || h.Host == nil { + return "" + } + + return *h.Host +} + +// GetEvent returns the Event field. +// +// When the provided Hook type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (h *Hook) GetEvent() string { + // return zero value if Hook type or Event field is nil + if h == nil || h.Event == nil { + return "" + } + + return *h.Event +} + +// GetEventAction returns the EventAction field. +// +// When the provided Hook type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (h *Hook) GetEventAction() string { + // return zero value if Hook type or EventAction field is nil + if h == nil || h.EventAction == nil { + return "" + } + + return *h.EventAction +} + +// GetBranch returns the Branch field. +// +// When the provided Hook type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (h *Hook) GetBranch() string { + // return zero value if Hook type or Branch field is nil + if h == nil || h.Branch == nil { + return "" + } + + return *h.Branch +} + +// GetError returns the Error field. +// +// When the provided Hook type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (h *Hook) GetError() string { + // return zero value if Hook type or Error field is nil + if h == nil || h.Error == nil { + return "" + } + + return *h.Error +} + +// GetStatus returns the Status field. +// +// When the provided Hook type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (h *Hook) GetStatus() string { + // return zero value if Hook type or Status field is nil + if h == nil || h.Status == nil { + return "" + } + + return *h.Status +} + +// GetLink returns the Link field. +// +// When the provided Hook type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (h *Hook) GetLink() string { + // return zero value if Hook type or Link field is nil + if h == nil || h.Link == nil { + return "" + } + + return *h.Link +} + +// GetWebhookID returns the WebhookID field. +// +// When the provided Hook type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (h *Hook) GetWebhookID() int64 { + // return zero value if Hook type or WebhookID field is nil + if h == nil || h.WebhookID == nil { + return 0 + } + + return *h.WebhookID +} + +// SetID sets the ID field. +// +// When the provided Hook type is nil, it +// will set nothing and immediately return. +func (h *Hook) SetID(v int64) { + // return if Hook type is nil + if h == nil { + return + } + + h.ID = &v +} + +// SetRepo sets the Repo field. +// +// When the provided Hook type is nil, it +// will set nothing and immediately return. +func (h *Hook) SetRepo(v *Repo) { + // return if Hook type is nil + if h == nil { + return + } + + h.Repo = v +} + +// SetBuild sets the Build field. +// +// When the provided Hook type is nil, it +// will set nothing and immediately return. +func (h *Hook) SetBuild(v *Build) { + // return if Hook type is nil + if h == nil { + return + } + + h.Build = v +} + +// SetNumber sets the Number field. +// +// When the provided Hook type is nil, it +// will set nothing and immediately return. +func (h *Hook) SetNumber(v int) { + // return if Hook type is nil + if h == nil { + return + } + + h.Number = &v +} + +// SetSourceID sets the SourceID field. +// +// When the provided Hook type is nil, it +// will set nothing and immediately return. +func (h *Hook) SetSourceID(v string) { + // return if Hook type is nil + if h == nil { + return + } + + h.SourceID = &v +} + +// SetCreated sets the Created field. +// +// When the provided Hook type is nil, it +// will set nothing and immediately return. +func (h *Hook) SetCreated(v int64) { + // return if Hook type is nil + if h == nil { + return + } + + h.Created = &v +} + +// SetHost sets the Host field. +// +// When the provided Hook type is nil, it +// will set nothing and immediately return. +func (h *Hook) SetHost(v string) { + // return if Hook type is nil + if h == nil { + return + } + + h.Host = &v +} + +// SetEvent sets the Event field. +// +// When the provided Hook type is nil, it +// will set nothing and immediately return. +func (h *Hook) SetEvent(v string) { + // return if Hook type is nil + if h == nil { + return + } + + h.Event = &v +} + +// SetEventAction sets the EventAction field. +// +// When the provided Hook type is nil, it +// will set nothing and immediately return. +func (h *Hook) SetEventAction(v string) { + // return if Hook type is nil + if h == nil { + return + } + + h.EventAction = &v +} + +// SetBranch sets the Branch field. +// +// When the provided Hook type is nil, it +// will set nothing and immediately return. +func (h *Hook) SetBranch(v string) { + // return if Hook type is nil + if h == nil { + return + } + + h.Branch = &v +} + +// SetError sets the Error field. +// +// When the provided Hook type is nil, it +// will set nothing and immediately return. +func (h *Hook) SetError(v string) { + // return if Hook type is nil + if h == nil { + return + } + + h.Error = &v +} + +// SetStatus sets the Status field. +// +// When the provided Hook type is nil, it +// will set nothing and immediately return. +func (h *Hook) SetStatus(v string) { + // return if Hook type is nil + if h == nil { + return + } + + h.Status = &v +} + +// SetLink sets the Link field. +// +// When the provided Hook type is nil, it +// will set nothing and immediately return. +func (h *Hook) SetLink(v string) { + // return if Hook type is nil + if h == nil { + return + } + + h.Link = &v +} + +// SetWebhookID sets the WebhookID field. +// +// When the provided Hook type is nil, it +// will set nothing and immediately return. +func (h *Hook) SetWebhookID(v int64) { + // return if Hook type is nil + if h == nil { + return + } + + h.WebhookID = &v +} + +// String implements the Stringer interface for the Hook type. +func (h *Hook) String() string { + return fmt.Sprintf(`{ + Branch: %s, + Build: %v, + Created: %d, + Error: %s, + Event: %s, + EventAction: %s, + Host: %s, + ID: %d, + Link: %s, + Number: %d, + Repo: %v, + SourceID: %s, + Status: %s, + WebhookID: %d, +}`, + h.GetBranch(), + h.GetBuild(), + h.GetCreated(), + h.GetError(), + h.GetEvent(), + h.GetEventAction(), + h.GetHost(), + h.GetID(), + h.GetLink(), + h.GetNumber(), + h.GetRepo(), + h.GetSourceID(), + h.GetStatus(), + h.GetWebhookID(), + ) +} diff --git a/api/types/hook_test.go b/api/types/hook_test.go new file mode 100644 index 000000000..c9dbad10e --- /dev/null +++ b/api/types/hook_test.go @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: Apache-2.0 + +package types + +import ( + "fmt" + "reflect" + "testing" + "time" + + "github.com/google/go-cmp/cmp" +) + +func TestTypes_Hook_Getters(t *testing.T) { + // setup tests + tests := []struct { + hook *Hook + want *Hook + }{ + { + hook: testHook(), + want: testHook(), + }, + { + hook: new(Hook), + want: new(Hook), + }, + } + + // run tests + for _, test := range tests { + if test.hook.GetID() != test.want.GetID() { + t.Errorf("GetID is %v, want %v", test.hook.GetID(), test.want.GetID()) + } + + if diff := cmp.Diff(test.hook.GetRepo(), test.want.GetRepo()); diff != "" { + t.Errorf("GetRepo is a mismatch (-got +want):\n%s", diff) + } + + if diff := cmp.Diff(test.hook.GetBuild(), test.want.GetBuild()); diff != "" { + t.Errorf("GetBuild is a mismatch (-got +want):\n%s", diff) + } + + if test.hook.GetNumber() != test.want.GetNumber() { + t.Errorf("GetNumber is %v, want %v", test.hook.GetNumber(), test.want.GetNumber()) + } + + if test.hook.GetSourceID() != test.want.GetSourceID() { + t.Errorf("GetSourceID is %v, want %v", test.hook.GetSourceID(), test.want.GetSourceID()) + } + + if test.hook.GetCreated() != test.want.GetCreated() { + t.Errorf("GetCreated is %v, want %v", test.hook.GetCreated(), test.want.GetCreated()) + } + + if test.hook.GetHost() != test.want.GetHost() { + t.Errorf("GetHost is %v, want %v", test.hook.GetHost(), test.want.GetHost()) + } + + if test.hook.GetEvent() != test.want.GetEvent() { + t.Errorf("GetEvent is %v, want %v", test.hook.GetEvent(), test.want.GetEvent()) + } + + if test.hook.GetEventAction() != test.want.GetEventAction() { + t.Errorf("GetEventAction is %v, want %v", test.hook.GetEventAction(), test.want.GetEventAction()) + } + + if test.hook.GetBranch() != test.want.GetBranch() { + t.Errorf("GetBranch is %v, want %v", test.hook.GetBranch(), test.want.GetBranch()) + } + + if test.hook.GetError() != test.want.GetError() { + t.Errorf("GetError is %v, want %v", test.hook.GetError(), test.want.GetError()) + } + + if test.hook.GetStatus() != test.want.GetStatus() { + t.Errorf("GetStatus is %v, want %v", test.hook.GetStatus(), test.want.GetStatus()) + } + + if test.hook.GetLink() != test.want.GetLink() { + t.Errorf("GetLink is %v, want %v", test.hook.GetLink(), test.want.GetLink()) + } + + if test.hook.GetWebhookID() != test.want.GetWebhookID() { + t.Errorf("GetWebhookID is %v, want %v", test.hook.GetWebhookID(), test.want.GetWebhookID()) + } + } +} + +func TestTypes_Hook_Setters(t *testing.T) { + // setup types + var h *Hook + + // setup tests + tests := []struct { + hook *Hook + want *Hook + }{ + { + hook: testHook(), + want: testHook(), + }, + { + hook: h, + want: new(Hook), + }, + } + + // run tests + for _, test := range tests { + test.hook.SetID(test.want.GetID()) + test.hook.SetRepo(test.want.GetRepo()) + test.hook.SetBuild(test.want.GetBuild()) + test.hook.SetNumber(test.want.GetNumber()) + test.hook.SetSourceID(test.want.GetSourceID()) + test.hook.SetCreated(test.want.GetCreated()) + test.hook.SetHost(test.want.GetHost()) + test.hook.SetEvent(test.want.GetEvent()) + test.hook.SetEventAction(test.want.GetEventAction()) + test.hook.SetBranch(test.want.GetBranch()) + test.hook.SetError(test.want.GetError()) + test.hook.SetStatus(test.want.GetStatus()) + test.hook.SetLink(test.want.GetLink()) + test.hook.SetWebhookID(test.want.GetWebhookID()) + + if test.hook.GetID() != test.want.GetID() { + t.Errorf("SetID is %v, want %v", test.hook.GetID(), test.want.GetID()) + } + + if diff := cmp.Diff(test.hook.GetRepo(), test.want.GetRepo()); diff != "" { + t.Errorf("SetRepo is a mismatch (-got +want):\n%s", diff) + } + + if diff := cmp.Diff(test.hook.GetBuild(), test.want.GetBuild()); diff != "" { + t.Errorf("SetBuild is a mismatch (-got +want):\n%s", diff) + } + + if test.hook.GetNumber() != test.want.GetNumber() { + t.Errorf("SetNumber is %v, want %v", test.hook.GetNumber(), test.want.GetNumber()) + } + + if test.hook.GetSourceID() != test.want.GetSourceID() { + t.Errorf("SetSourceID is %v, want %v", test.hook.GetSourceID(), test.want.GetSourceID()) + } + + if test.hook.GetCreated() != test.want.GetCreated() { + t.Errorf("SetCreated is %v, want %v", test.hook.GetCreated(), test.want.GetCreated()) + } + + if test.hook.GetHost() != test.want.GetHost() { + t.Errorf("SetHost is %v, want %v", test.hook.GetHost(), test.want.GetHost()) + } + + if test.hook.GetEvent() != test.want.GetEvent() { + t.Errorf("SetEvent is %v, want %v", test.hook.GetEvent(), test.want.GetEvent()) + } + + if test.hook.GetEventAction() != test.want.GetEventAction() { + t.Errorf("SetEventAction is %v, want %v", test.hook.GetEventAction(), test.want.GetEventAction()) + } + + if test.hook.GetBranch() != test.want.GetBranch() { + t.Errorf("SetBranch is %v, want %v", test.hook.GetBranch(), test.want.GetBranch()) + } + + if test.hook.GetError() != test.want.GetError() { + t.Errorf("SetError is %v, want %v", test.hook.GetError(), test.want.GetError()) + } + + if test.hook.GetStatus() != test.want.GetStatus() { + t.Errorf("SetStatus is %v, want %v", test.hook.GetStatus(), test.want.GetStatus()) + } + + if test.hook.GetLink() != test.want.GetLink() { + t.Errorf("SetLink is %v, want %v", test.hook.GetLink(), test.want.GetLink()) + } + + if test.hook.GetWebhookID() != test.want.GetWebhookID() { + t.Errorf("SetWebhookID is %v, want %v", test.hook.GetWebhookID(), test.want.GetWebhookID()) + } + } +} + +func TestTypes_Hook_String(t *testing.T) { + // setup types + h := testHook() + + want := fmt.Sprintf(`{ + Branch: %s, + Build: %v, + Created: %d, + Error: %s, + Event: %s, + EventAction: %s, + Host: %s, + ID: %d, + Link: %s, + Number: %d, + Repo: %v, + SourceID: %s, + Status: %s, + WebhookID: %d, +}`, + h.GetBranch(), + h.GetBuild(), + h.GetCreated(), + h.GetError(), + h.GetEvent(), + h.GetEventAction(), + h.GetHost(), + h.GetID(), + h.GetLink(), + h.GetNumber(), + h.GetRepo(), + h.GetSourceID(), + h.GetStatus(), + h.GetWebhookID(), + ) + + // run test + got := h.String() + + if !reflect.DeepEqual(got, want) { + t.Errorf("String is %v, want %v", got, want) + } +} + +// testHook is a test helper function to create a Hook +// type with all fields set to a fake value. +func testHook() *Hook { + h := new(Hook) + + h.SetID(1) + h.SetRepo(testRepo()) + h.SetBuild(testBuild()) + h.SetNumber(1) + h.SetSourceID("c8da1302-07d6-11ea-882f-4893bca275b8") + h.SetCreated(time.Now().UTC().Unix()) + h.SetHost("github.com") + h.SetEvent("push") + h.SetEventAction("") + h.SetBranch("main") + h.SetError("") + h.SetStatus("success") + h.SetLink("https://github.com/github/octocat/settings/hooks/1") + h.SetWebhookID(123456) + + return h +} diff --git a/api/webhook/post.go b/api/webhook/post.go index 0f770021d..c83a600be 100644 --- a/api/webhook/post.go +++ b/api/webhook/post.go @@ -237,7 +237,7 @@ func PostWebhook(c *gin.Context) { // set the RepoID fields b.SetRepo(repo) - h.SetRepoID(repo.GetID()) + h.SetRepo(repo) // number of times to retry retryLimit := 3 @@ -423,8 +423,8 @@ func PostWebhook(c *gin.Context) { // capture the build and repo from the items b = item.Build - // set hook build_id to the generated build id - h.SetBuildID(b.GetID()) + // set hook build + h.SetBuild(b) // if event is deployment, update the deployment record to include this build if strings.EqualFold(b.GetEvent(), constants.EventDeploy) { @@ -618,7 +618,7 @@ func PostWebhook(c *gin.Context) { // the database resources with any relevant changes resulting from the event, such as name changes, transfers, etc. // // the caller is responsible for returning errors to the client. -func handleRepositoryEvent(ctx context.Context, c *gin.Context, m *internal.Metadata, h *library.Hook, r *types.Repo) (*types.Repo, error) { +func handleRepositoryEvent(ctx context.Context, c *gin.Context, m *internal.Metadata, h *types.Hook, r *types.Repo) (*types.Repo, error) { l := c.MustGet("logger").(*logrus.Entry) l = l.WithFields(logrus.Fields{ @@ -687,7 +687,7 @@ func handleRepositoryEvent(ctx context.Context, c *gin.Context, m *internal.Meta ) } - h.SetRepoID(dbRepo.GetID()) + h.SetRepo(dbRepo) // the only edits to a repo that impact Vela are to these three fields if !strings.EqualFold(dbRepo.GetBranch(), r.GetBranch()) { @@ -732,7 +732,7 @@ func handleRepositoryEvent(ctx context.Context, c *gin.Context, m *internal.Meta // associated with that repo as well as build links for the UI. // // the caller is responsible for returning errors to the client. -func RenameRepository(ctx context.Context, h *library.Hook, r *types.Repo, c *gin.Context, m *internal.Metadata) (*types.Repo, error) { +func RenameRepository(ctx context.Context, h *types.Hook, r *types.Repo, c *gin.Context, m *internal.Metadata) (*types.Repo, error) { l := c.MustGet("logger").(*logrus.Entry) l = l.WithFields(logrus.Fields{ @@ -748,13 +748,13 @@ func RenameRepository(ctx context.Context, h *library.Hook, r *types.Repo, c *gi } // get the repo from the database using repo id of matching hook - dbR, err := database.FromContext(c).GetRepo(ctx, hook.GetRepoID()) + dbR, err := database.FromContext(c).GetRepo(ctx, hook.GetRepo().GetID()) if err != nil { - return nil, fmt.Errorf("%s: failed to get repo %d from database", baseErr, hook.GetRepoID()) + return nil, fmt.Errorf("%s: failed to get repo %d from database", baseErr, hook.GetRepo().GetID()) } // update hook object which will be added to DB upon reaching deferred function in PostWebhook - h.SetRepoID(r.GetID()) + h.SetRepo(r) // send API call to capture the last hook for the repo lastHook, err := database.FromContext(c).LastHookForRepo(ctx, dbR) diff --git a/constants/driver.go b/constants/driver.go new file mode 100644 index 000000000..e42b924c5 --- /dev/null +++ b/constants/driver.go @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: Apache-2.0 + +package constants + +// Server database drivers. +const ( + // DriverPostgres defines the driver type when integrating with a PostgreSQL database. + DriverPostgres = "postgres" + + // DriverSqlite defines the driver type when integrating with a SQLite database. + DriverSqlite = "sqlite3" +) + +// Worker executor drivers. +const ( + // DriverDarwin defines the driver type when integrating with a darwin distribution. + DriverDarwin = "darwin" + + // DriverLinux defines the driver type when integrating with a linux distribution. + DriverLinux = "linux" + + // DriverLocal defines the driver type when integrating with a local system. + DriverLocal = "local" + + // DriverWindows defines the driver type when integrating with a windows distribution. + DriverWindows = "windows" +) + +// Server and worker queue drivers. +const ( + + // DriverKafka defines the driver type when integrating with a Kafka queue. + DriverKafka = "kafka" + + // DriverRedis defines the driver type when integrating with a Redis queue. + DriverRedis = "redis" +) + +// Worker runtime drivers. +const ( + // DriverDocker defines the driver type when integrating with a Docker runtime. + DriverDocker = "docker" + + // DriverKubernetes defines the driver type when integrating with a Kubernetes runtime. + DriverKubernetes = "kubernetes" +) + +// Server and worker secret drivers. +const ( + // DriverNative defines the driver type when integrating with a Vela secret service. + DriverNative = "native" + + // DriverVault defines the driver type when integrating with a Vault secret service. + DriverVault = "vault" +) + +// Server source drivers. +const ( + // DriverGitHub defines the driver type when integrating with a Github source code system. + DriverGithub = "github" + + // DriverGitLab defines the driver type when integrating with a Gitlab source code system. + DriverGitlab = "gitlab" +) diff --git a/constants/repo.go b/constants/repo.go new file mode 100644 index 000000000..c6e8277d8 --- /dev/null +++ b/constants/repo.go @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: Apache-2.0 + +package constants + +// Repo get pipeline types. +const ( + // PipelineTypeYAML defines the pipeline type for allowing users + // in Vela to control their pipeline being compiled as yaml. + PipelineTypeYAML = "yaml" + + // PipelineTypeGo defines the pipeline type for allowing users + // in Vela to control their pipeline being compiled as Go templates. + PipelineTypeGo = "go" + + // PipelineTypeStarlark defines the pipeline type for allowing users + // in Vela to control their pipeline being compiled as Starlark templates. + PipelineTypeStarlark = "starlark" +) + +// Repo ApproveBuild types. +const ( + // ApproveForkAlways defines the CI strategy of having a repo administrator approve + // all builds triggered from a forked PR. + ApproveForkAlways = "fork-always" + + // ApproveForkNoWrite defines the CI strategy of having a repo administrator approve + // all builds triggered from a forked PR where the author does not have write access. + ApproveForkNoWrite = "fork-no-write" + + // ApproveOnce defines the CI strategy of having a repo administrator approve + // all builds triggered from an outside contributor if this is their first time contributing. + ApproveOnce = "first-time" + + // ApproveNever defines the CI strategy of never having to approve CI builds from outside contributors. + ApproveNever = "never" +) diff --git a/constants/table.go b/constants/table.go index 736335482..871225fe1 100644 --- a/constants/table.go +++ b/constants/table.go @@ -3,9 +3,48 @@ package constants // Database tables. const ( + // TableBuild defines the table type for the database builds table. + TableBuild = "builds" + + // TableBuildExecutable defines the table type for the database build_executables table. + TableBuildExecutable = "build_executables" + // TableDashboard defines the table type for the database dashboards table. TableDashboard = "dashboards" + // TableDeployment defines the table type for the database deployments table. + TableDeployment = "deployments" + + // TableHook defines the table type for the database hooks table. + TableHook = "hooks" + // TableJWK defines the table type for the database jwks table. TableJWK = "jwks" + + // TableLog defines the table type for the database logs table. + TableLog = "logs" + + // TablePipeline defines the table type for the database pipelines table. + TablePipeline = "pipelines" + + // TableRepo defines the table type for the database repos table. + TableRepo = "repos" + + // TableSchedule defines the table type for the database schedules table. + TableSchedule = "schedules" + + // TableSecret defines the table type for the database secrets table. + TableSecret = "secrets" + + // TableService defines the table type for the database services table. + TableService = "services" + + // TableStep defines the table type for the database steps table. + TableStep = "steps" + + // TableUser defines the table type for the database users table. + TableUser = "users" + + // TableWorker defines the table type for the database workers table. + TableWorker = "workers" ) diff --git a/database/hook/count.go b/database/hook/count.go index 9f3e3c264..d10cfc6f4 100644 --- a/database/hook/count.go +++ b/database/hook/count.go @@ -5,7 +5,7 @@ package hook import ( "context" - "github.com/go-vela/types/constants" + "github.com/go-vela/server/constants" ) // CountHooks gets the count of all hooks from the database. diff --git a/database/hook/count_repo.go b/database/hook/count_repo.go index 456019fc1..0d97ec42f 100644 --- a/database/hook/count_repo.go +++ b/database/hook/count_repo.go @@ -8,7 +8,7 @@ import ( "github.com/sirupsen/logrus" api "github.com/go-vela/server/api/types" - "github.com/go-vela/types/constants" + "github.com/go-vela/server/constants" ) // CountHooksForRepo gets the count of hooks by repo ID from the database. diff --git a/database/hook/count_repo_test.go b/database/hook/count_repo_test.go index e6b8c8d43..a8dfdfdb5 100644 --- a/database/hook/count_repo_test.go +++ b/database/hook/count_repo_test.go @@ -9,34 +9,47 @@ import ( "github.com/DATA-DOG/go-sqlmock" + "github.com/go-vela/server/constants" "github.com/go-vela/server/database/testutils" + "github.com/go-vela/server/database/types" ) func TestHook_Engine_CountHooksForRepo(t *testing.T) { // setup types + _repoOne := testutils.APIRepo() + _repoOne.SetID(1) + _repoOne.SetOrg("foo") + _repoOne.SetName("bar") + _repoOne.SetFullName("foo/bar") + + _repoTwo := testutils.APIRepo() + _repoTwo.SetID(2) + _repoTwo.SetOrg("foo") + _repoTwo.SetName("baz") + _repoTwo.SetFullName("foo/baz") + + _buildOne := testutils.APIBuild() + _buildOne.SetID(1) + + _buildTwo := testutils.APIBuild() + _buildTwo.SetID(2) + _hookOne := testutils.APIHook() _hookOne.SetID(1) - _hookOne.SetRepoID(1) - _hookOne.SetBuildID(1) + _hookOne.SetRepo(_repoOne) + _hookOne.SetBuild(_buildOne) _hookOne.SetNumber(1) _hookOne.SetSourceID("c8da1302-07d6-11ea-882f-4893bca275b8") _hookOne.SetWebhookID(1) _hookTwo := testutils.APIHook() _hookTwo.SetID(2) - _hookTwo.SetRepoID(2) - _hookTwo.SetBuildID(2) + _hookTwo.SetRepo(_repoTwo) + _hookTwo.SetBuild(_buildTwo) _hookTwo.SetNumber(2) _hookTwo.SetSourceID("c8da1302-07d6-11ea-882f-4893bca275b8") _hookTwo.SetWebhookID(1) - _repo := testutils.APIRepo() - _repo.SetID(1) - _repo.GetOwner().SetID(1) - _repo.SetOrg("foo") - _repo.SetName("bar") - _repo.SetFullName("foo/bar") - _postgres, _mock := testPostgres(t) defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() @@ -59,6 +72,21 @@ func TestHook_Engine_CountHooksForRepo(t *testing.T) { t.Errorf("unable to create test hook for sqlite: %v", err) } + err = _sqlite.client.AutoMigrate(&types.Repo{}) + if err != nil { + t.Errorf("unable to create repo table for sqlite: %v", err) + } + + err = _sqlite.client.Table(constants.TableRepo).Create(types.RepoFromAPI(_repoOne)).Error + if err != nil { + t.Errorf("unable to create test repo for sqlite: %v", err) + } + + err = _sqlite.client.Table(constants.TableRepo).Create(types.RepoFromAPI(_repoTwo)).Error + if err != nil { + t.Errorf("unable to create test repo for sqlite: %v", err) + } + // setup tests tests := []struct { failure bool @@ -83,7 +111,7 @@ func TestHook_Engine_CountHooksForRepo(t *testing.T) { // run tests for _, test := range tests { t.Run(test.name, func(t *testing.T) { - got, err := test.database.CountHooksForRepo(context.TODO(), _repo) + got, err := test.database.CountHooksForRepo(context.TODO(), _repoOne) if test.failure { if err == nil { diff --git a/database/hook/count_test.go b/database/hook/count_test.go index 20c022217..52a3f5691 100644 --- a/database/hook/count_test.go +++ b/database/hook/count_test.go @@ -14,18 +14,36 @@ import ( func TestHook_Engine_CountHooks(t *testing.T) { // setup types + _repoOne := testutils.APIRepo() + _repoOne.SetID(1) + _repoOne.SetOrg("foo") + _repoOne.SetName("bar") + _repoOne.SetFullName("foo/bar") + + _repoTwo := testutils.APIRepo() + _repoTwo.SetID(2) + _repoTwo.SetOrg("foo") + _repoTwo.SetName("baz") + _repoTwo.SetFullName("foo/baz") + + _buildOne := testutils.APIBuild() + _buildOne.SetID(1) + + _buildTwo := testutils.APIBuild() + _buildTwo.SetID(2) + _hookOne := testutils.APIHook() _hookOne.SetID(1) - _hookOne.SetRepoID(1) - _hookOne.SetBuildID(1) + _hookOne.SetRepo(_repoOne) + _hookOne.SetBuild(_buildOne) _hookOne.SetNumber(1) _hookOne.SetSourceID("c8da1302-07d6-11ea-882f-4893bca275b8") _hookOne.SetWebhookID(1) _hookTwo := testutils.APIHook() _hookTwo.SetID(2) - _hookTwo.SetRepoID(1) - _hookTwo.SetBuildID(2) + _hookTwo.SetRepo(_repoTwo) + _hookTwo.SetBuild(_buildTwo) _hookTwo.SetNumber(2) _hookTwo.SetSourceID("c8da1302-07d6-11ea-882f-4893bca275b8") _hookTwo.SetWebhookID(1) diff --git a/database/hook/create.go b/database/hook/create.go index badb47e82..293e1623b 100644 --- a/database/hook/create.go +++ b/database/hook/create.go @@ -7,35 +7,33 @@ import ( "github.com/sirupsen/logrus" - "github.com/go-vela/types/constants" - "github.com/go-vela/types/database" - "github.com/go-vela/types/library" + api "github.com/go-vela/server/api/types" + "github.com/go-vela/server/constants" + "github.com/go-vela/server/database/types" ) // CreateHook creates a new hook in the database. -func (e *engine) CreateHook(ctx context.Context, h *library.Hook) (*library.Hook, error) { +func (e *engine) CreateHook(ctx context.Context, h *api.Hook) (*api.Hook, error) { e.logger.WithFields(logrus.Fields{ "hook": h.GetNumber(), }).Tracef("creating hook %d", h.GetNumber()) - // cast the library type to database type - // - // https://pkg.go.dev/github.com/go-vela/types/database#HookFromLibrary - hook := database.HookFromLibrary(h) + hook := types.HookFromAPI(h) - // validate the necessary fields are populated - // - // https://pkg.go.dev/github.com/go-vela/types/database#Hook.Validate err := hook.Validate() if err != nil { return nil, err } - result := e.client. - WithContext(ctx). - Table(constants.TableHook). - Create(hook) - // send query to the database - return hook.ToLibrary(), result.Error + err = e.client.WithContext(ctx).Table(constants.TableHook).Create(hook).Error + if err != nil { + return nil, err + } + + result := hook.ToAPI() + result.SetRepo(h.GetRepo()) + result.SetBuild(h.GetBuild()) + + return result, nil } diff --git a/database/hook/create_test.go b/database/hook/create_test.go index 2dd34cb10..ee965f79e 100644 --- a/database/hook/create_test.go +++ b/database/hook/create_test.go @@ -13,10 +13,16 @@ import ( func TestHook_Engine_CreateHook(t *testing.T) { // setup types + _repo := testutils.APIRepo() + _repo.SetID(1) + + _build := testutils.APIBuild() + _build.SetID(1) + _hook := testutils.APIHook() _hook.SetID(1) - _hook.SetRepoID(1) - _hook.SetBuildID(1) + _hook.SetRepo(_repo) + _hook.SetBuild(_build) _hook.SetNumber(1) _hook.SetSourceID("c8da1302-07d6-11ea-882f-4893bca275b8") _hook.SetWebhookID(1) diff --git a/database/hook/delete.go b/database/hook/delete.go index 6eab73780..51c72a0c6 100644 --- a/database/hook/delete.go +++ b/database/hook/delete.go @@ -7,21 +7,18 @@ import ( "github.com/sirupsen/logrus" - "github.com/go-vela/types/constants" - "github.com/go-vela/types/database" - "github.com/go-vela/types/library" + api "github.com/go-vela/server/api/types" + "github.com/go-vela/server/constants" + "github.com/go-vela/server/database/types" ) // DeleteHook deletes an existing hook from the database. -func (e *engine) DeleteHook(ctx context.Context, h *library.Hook) error { +func (e *engine) DeleteHook(ctx context.Context, h *api.Hook) error { e.logger.WithFields(logrus.Fields{ "hook": h.GetNumber(), }).Tracef("deleting hook %d", h.GetNumber()) - // cast the library type to database type - // - // https://pkg.go.dev/github.com/go-vela/types/database#HookFromLibrary - hook := database.HookFromLibrary(h) + hook := types.HookFromAPI(h) // send query to the database return e.client. diff --git a/database/hook/delete_test.go b/database/hook/delete_test.go index f89e44116..cfea6784c 100644 --- a/database/hook/delete_test.go +++ b/database/hook/delete_test.go @@ -8,15 +8,22 @@ import ( "github.com/DATA-DOG/go-sqlmock" + api "github.com/go-vela/server/api/types" "github.com/go-vela/server/database/testutils" ) func TestHook_Engine_DeleteHook(t *testing.T) { // setup types + _repo := testutils.APIRepo() + _repo.SetID(1) + + _build := testutils.APIBuild() + _build.SetID(1) + _hook := testutils.APIHook() _hook.SetID(1) - _hook.SetRepoID(1) - _hook.SetBuildID(1) + _hook.SetRepo(_repo) + _hook.SetBuild(_build) _hook.SetNumber(1) _hook.SetSourceID("c8da1302-07d6-11ea-882f-4893bca275b8") _hook.SetWebhookID(1) @@ -32,10 +39,7 @@ func TestHook_Engine_DeleteHook(t *testing.T) { _sqlite := testSqlite(t) defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() - _, err := _sqlite.CreateHook(context.TODO(), _hook) - if err != nil { - t.Errorf("unable to create test hook for sqlite: %v", err) - } + sqlitePopulateTables(t, _sqlite, []*api.Hook{_hook}, nil, nil, nil) // setup tests tests := []struct { @@ -58,7 +62,7 @@ func TestHook_Engine_DeleteHook(t *testing.T) { // run tests for _, test := range tests { t.Run(test.name, func(t *testing.T) { - err = test.database.DeleteHook(context.TODO(), _hook) + err := test.database.DeleteHook(context.TODO(), _hook) if test.failure { if err == nil { diff --git a/database/hook/get.go b/database/hook/get.go index afe9b920d..27e023139 100644 --- a/database/hook/get.go +++ b/database/hook/get.go @@ -5,22 +5,25 @@ package hook import ( "context" - "github.com/go-vela/types/constants" - "github.com/go-vela/types/database" - "github.com/go-vela/types/library" + api "github.com/go-vela/server/api/types" + "github.com/go-vela/server/constants" + "github.com/go-vela/server/database/types" ) // GetHook gets a hook by ID from the database. -func (e *engine) GetHook(ctx context.Context, id int64) (*library.Hook, error) { +func (e *engine) GetHook(ctx context.Context, id int64) (*api.Hook, error) { e.logger.Tracef("getting hook %d", id) // variable to store query results - h := new(database.Hook) + h := new(types.Hook) // send query to the database and store result in variable err := e.client. WithContext(ctx). Table(constants.TableHook). + Preload("Repo"). + Preload("Repo.Owner"). + Preload("Build"). Where("id = ?", id). Take(h). Error @@ -28,8 +31,10 @@ func (e *engine) GetHook(ctx context.Context, id int64) (*library.Hook, error) { return nil, err } - // return the hook - // - // https://pkg.go.dev/github.com/go-vela/types/database#Hook.ToLibrary - return h.ToLibrary(), nil + err = h.Repo.Decrypt(e.config.EncryptionKey) + if err != nil { + e.logger.Errorf("unable to decrypt repo for hook %d: %v", h.ID.Int64, err) + } + + return h.ToAPI(), nil } diff --git a/database/hook/get_repo.go b/database/hook/get_repo.go index 61afe28b2..896275a16 100644 --- a/database/hook/get_repo.go +++ b/database/hook/get_repo.go @@ -8,13 +8,12 @@ import ( "github.com/sirupsen/logrus" api "github.com/go-vela/server/api/types" - "github.com/go-vela/types/constants" - "github.com/go-vela/types/database" - "github.com/go-vela/types/library" + "github.com/go-vela/server/constants" + "github.com/go-vela/server/database/types" ) // GetHookForRepo gets a hook by repo ID and number from the database. -func (e *engine) GetHookForRepo(ctx context.Context, r *api.Repo, number int) (*library.Hook, error) { +func (e *engine) GetHookForRepo(ctx context.Context, r *api.Repo, number int) (*api.Hook, error) { e.logger.WithFields(logrus.Fields{ "hook": number, "org": r.GetOrg(), @@ -22,12 +21,15 @@ func (e *engine) GetHookForRepo(ctx context.Context, r *api.Repo, number int) (* }).Tracef("getting hook %s/%d", r.GetFullName(), number) // variable to store query results - h := new(database.Hook) + h := new(types.Hook) // send query to the database and store result in variable err := e.client. WithContext(ctx). Table(constants.TableHook). + Preload("Repo"). + Preload("Repo.Owner"). + Preload("Build"). Where("repo_id = ?", r.GetID()). Where("number = ?", number). Take(h). @@ -36,8 +38,10 @@ func (e *engine) GetHookForRepo(ctx context.Context, r *api.Repo, number int) (* return nil, err } - // return the hook - // - // https://pkg.go.dev/github.com/go-vela/types/database#Hook.ToLibrary - return h.ToLibrary(), nil + err = h.Repo.Decrypt(e.config.EncryptionKey) + if err != nil { + e.logger.Errorf("unable to decrypt repo %s/%s: %v", r.GetOrg(), r.GetName(), err) + } + + return h.ToAPI(), nil } diff --git a/database/hook/get_repo_test.go b/database/hook/get_repo_test.go index df2dc6d7f..eb65a23db 100644 --- a/database/hook/get_repo_test.go +++ b/database/hook/get_repo_test.go @@ -4,31 +4,52 @@ package hook import ( "context" - "reflect" "testing" "github.com/DATA-DOG/go-sqlmock" + "github.com/google/go-cmp/cmp" + api "github.com/go-vela/server/api/types" + "github.com/go-vela/server/constants" "github.com/go-vela/server/database/testutils" - "github.com/go-vela/types/library" ) func TestHook_Engine_GetHookForRepo(t *testing.T) { // setup types - _hook := testutils.APIHook() - _hook.SetID(1) - _hook.SetRepoID(1) - _hook.SetBuildID(1) - _hook.SetNumber(1) - _hook.SetSourceID("c8da1302-07d6-11ea-882f-4893bca275b8") - _hook.SetWebhookID(1) + _owner := testutils.APIUser().Crop() + _owner.SetID(1) + _owner.SetName("foo") + _owner.SetToken("bar") _repo := testutils.APIRepo() _repo.SetID(1) - _repo.GetOwner().SetID(1) + _repo.SetOwner(_owner) + _repo.SetHash("baz") _repo.SetOrg("foo") _repo.SetName("bar") _repo.SetFullName("foo/bar") + _repo.SetVisibility("public") + _repo.SetAllowEvents(api.NewEventsFromMask(1)) + _repo.SetPipelineType(constants.PipelineTypeYAML) + _repo.SetTopics([]string{}) + + _repoBuild := new(api.Repo) + _repoBuild.SetID(1) + + _build := testutils.APIBuild() + _build.SetID(1) + _build.SetRepo(_repoBuild) + _build.SetNumber(1) + _build.SetDeployNumber(0) + _build.SetDeployPayload(nil) + + _hook := testutils.APIHook() + _hook.SetID(1) + _hook.SetRepo(_repo) + _hook.SetBuild(_build) + _hook.SetNumber(1) + _hook.SetSourceID("c8da1302-07d6-11ea-882f-4893bca275b8") + _hook.SetWebhookID(1) _postgres, _mock := testPostgres(t) defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() @@ -38,23 +59,42 @@ func TestHook_Engine_GetHookForRepo(t *testing.T) { []string{"id", "repo_id", "build_id", "number", "source_id", "created", "host", "event", "event_action", "branch", "error", "status", "link", "webhook_id"}). AddRow(1, 1, 1, 1, "c8da1302-07d6-11ea-882f-4893bca275b8", 0, "", "", "", "", "", "", "", 1) + _buildRows := sqlmock.NewRows( + []string{"id", "repo_id", "pipeline_id", "number", "parent", "event", "event_action", "status", "error", "enqueued", "created", "started", "finished", "deploy", "deploy_number", "deploy_payload", "clone", "source", "title", "message", "commit", "sender", "author", "email", "link", "branch", "ref", "base_ref", "head_ref", "host", "runtime", "distribution", "timestamp"}). + AddRow(1, 1, nil, 1, 0, "", "", "", "", 0, 0, 0, 0, "", 0, nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0) + + _repoRows := sqlmock.NewRows( + []string{"id", "user_id", "hash", "org", "name", "full_name", "link", "clone", "branch", "topics", "build_limit", "timeout", "counter", "visibility", "private", "trusted", "active", "allow_events", "pipeline_type", "previous_name", "approve_build"}). + AddRow(1, 1, "baz", "foo", "bar", "foo/bar", "", "", "", "{}", 0, 0, 0, "public", false, false, false, 1, "yaml", "", "") + + _userRows := sqlmock.NewRows( + []string{"id", "name", "token", "hash", "active", "admin"}). + AddRow(1, "foo", "bar", "baz", false, false) + // ensure the mock expects the query _mock.ExpectQuery(`SELECT * FROM "hooks" WHERE repo_id = $1 AND number = $2 LIMIT $3`).WithArgs(1, 1, 1).WillReturnRows(_rows) + _mock.ExpectQuery(`SELECT * FROM "builds" WHERE "builds"."id" = $1`).WithArgs(1).WillReturnRows(_buildRows) + _mock.ExpectQuery(`SELECT * FROM "repos" WHERE "repos"."id" = $1`).WithArgs(1).WillReturnRows(_repoRows) + _mock.ExpectQuery(`SELECT * FROM "users" WHERE "users"."id" = $1`).WithArgs(1).WillReturnRows(_userRows) _sqlite := testSqlite(t) defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() - _, err := _sqlite.CreateHook(context.TODO(), _hook) - if err != nil { - t.Errorf("unable to create test hook for sqlite: %v", err) - } + sqlitePopulateTables( + t, + _sqlite, + []*api.Hook{_hook}, + []*api.User{_owner}, + []*api.Repo{_repo}, + []*api.Build{_build}, + ) // setup tests tests := []struct { failure bool name string database *engine - want *library.Hook + want *api.Hook }{ { failure: false, @@ -87,8 +127,8 @@ func TestHook_Engine_GetHookForRepo(t *testing.T) { t.Errorf("GetHookForRepo for %s returned err: %v", test.name, err) } - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetHookForRepo for %s is %v, want %v", test.name, got, test.want) + if diff := cmp.Diff(test.want, got); diff != "" { + t.Errorf("GetHookForRepo for %s is a mismatch (-want +got):\n%s", test.name, diff) } }) } diff --git a/database/hook/get_test.go b/database/hook/get_test.go index a33f144c2..9e6664784 100644 --- a/database/hook/get_test.go +++ b/database/hook/get_test.go @@ -4,21 +4,48 @@ package hook import ( "context" - "reflect" "testing" "github.com/DATA-DOG/go-sqlmock" + "github.com/google/go-cmp/cmp" + api "github.com/go-vela/server/api/types" + "github.com/go-vela/server/constants" "github.com/go-vela/server/database/testutils" - "github.com/go-vela/types/library" ) func TestHook_Engine_GetHook(t *testing.T) { // setup types + _owner := testutils.APIUser().Crop() + _owner.SetID(1) + _owner.SetName("foo") + _owner.SetToken("bar") + + _repo := testutils.APIRepo() + _repo.SetID(1) + _repo.SetOwner(_owner) + _repo.SetHash("baz") + _repo.SetOrg("foo") + _repo.SetName("bar") + _repo.SetFullName("foo/bar") + _repo.SetVisibility("public") + _repo.SetAllowEvents(api.NewEventsFromMask(1)) + _repo.SetPipelineType(constants.PipelineTypeYAML) + _repo.SetTopics([]string{}) + + _repoBuild := new(api.Repo) + _repoBuild.SetID(1) + + _build := testutils.APIBuild() + _build.SetID(1) + _build.SetRepo(_repoBuild) + _build.SetNumber(1) + _build.SetDeployPayload(nil) + _hook := testutils.APIHook() _hook.SetID(1) - _hook.SetRepoID(1) - _hook.SetBuildID(1) + _hook.SetRepo(_repo) + _hook.SetBuild(_build) _hook.SetNumber(1) _hook.SetSourceID("c8da1302-07d6-11ea-882f-4893bca275b8") _hook.SetWebhookID(1) @@ -31,23 +58,35 @@ func TestHook_Engine_GetHook(t *testing.T) { []string{"id", "repo_id", "build_id", "number", "source_id", "created", "host", "event", "event_action", "branch", "error", "status", "link", "webhook_id"}, ).AddRow(1, 1, 1, 1, "c8da1302-07d6-11ea-882f-4893bca275b8", 0, "", "", "", "", "", "", "", 1) + _buildRows := sqlmock.NewRows( + []string{"id", "repo_id", "pipeline_id", "number", "parent", "event", "event_action", "status", "error", "enqueued", "created", "started", "finished", "deploy", "deploy_number", "deploy_payload", "clone", "source", "title", "message", "commit", "sender", "author", "email", "link", "branch", "ref", "base_ref", "head_ref", "host", "runtime", "distribution", "timestamp"}). + AddRow(1, 1, nil, 1, 0, "", "", "", "", 0, 0, 0, 0, "", 0, nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0) + + _repoRows := sqlmock.NewRows( + []string{"id", "user_id", "hash", "org", "name", "full_name", "link", "clone", "branch", "topics", "build_limit", "timeout", "counter", "visibility", "private", "trusted", "active", "allow_events", "pipeline_type", "previous_name", "approve_build"}). + AddRow(1, 1, "baz", "foo", "bar", "foo/bar", "", "", "", "{}", 0, 0, 0, "public", false, false, false, 1, "yaml", "", "") + + _userRows := sqlmock.NewRows( + []string{"id", "name", "token", "hash", "active", "admin"}). + AddRow(1, "foo", "bar", "baz", false, false) + // ensure the mock expects the query _mock.ExpectQuery(`SELECT * FROM "hooks" WHERE id = $1 LIMIT $2`).WithArgs(1, 1).WillReturnRows(_rows) + _mock.ExpectQuery(`SELECT * FROM "builds" WHERE "builds"."id" = $1`).WithArgs(1).WillReturnRows(_buildRows) + _mock.ExpectQuery(`SELECT * FROM "repos" WHERE "repos"."id" = $1`).WithArgs(1).WillReturnRows(_repoRows) + _mock.ExpectQuery(`SELECT * FROM "users" WHERE "users"."id" = $1`).WithArgs(1).WillReturnRows(_userRows) _sqlite := testSqlite(t) defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() - _, err := _sqlite.CreateHook(context.TODO(), _hook) - if err != nil { - t.Errorf("unable to create test hook for sqlite: %v", err) - } + sqlitePopulateTables(t, _sqlite, []*api.Hook{_hook}, []*api.User{_owner}, []*api.Repo{_repo}, []*api.Build{_build}) // setup tests tests := []struct { failure bool name string database *engine - want *library.Hook + want *api.Hook }{ { failure: false, @@ -80,8 +119,8 @@ func TestHook_Engine_GetHook(t *testing.T) { t.Errorf("GetHook for %s returned err: %v", test.name, err) } - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetHook for %s is %v, want %v", test.name, got, test.want) + if diff := cmp.Diff(test.want, got); diff != "" { + t.Errorf("GetHook for %s: -want, +got: %s", test.name, diff) } }) } diff --git a/database/hook/get_webhook.go b/database/hook/get_webhook.go index 47ea57f6c..2cae36b93 100644 --- a/database/hook/get_webhook.go +++ b/database/hook/get_webhook.go @@ -5,22 +5,25 @@ package hook import ( "context" - "github.com/go-vela/types/constants" - "github.com/go-vela/types/database" - "github.com/go-vela/types/library" + api "github.com/go-vela/server/api/types" + "github.com/go-vela/server/constants" + "github.com/go-vela/server/database/types" ) // GetHookByWebhookID gets a single hook with a matching webhook id in the database. -func (e *engine) GetHookByWebhookID(ctx context.Context, webhookID int64) (*library.Hook, error) { +func (e *engine) GetHookByWebhookID(ctx context.Context, webhookID int64) (*api.Hook, error) { e.logger.Tracef("getting a hook with webhook id %d", webhookID) // variable to store query results - h := new(database.Hook) + h := new(types.Hook) // send query to the database and store result in variable err := e.client. WithContext(ctx). Table(constants.TableHook). + Preload("Repo"). + Preload("Repo.Owner"). + Preload("Build"). Where("webhook_id = ?", webhookID). Take(h). Error @@ -28,8 +31,10 @@ func (e *engine) GetHookByWebhookID(ctx context.Context, webhookID int64) (*libr return nil, err } - // return the hook - // - // https://pkg.go.dev/github.com/go-vela/types/database#Hook.ToLibrary - return h.ToLibrary(), nil + err = h.Repo.Decrypt(e.config.EncryptionKey) + if err != nil { + e.logger.Errorf("unable to decrypt repo for hook %d: %v", h.ID.Int64, err) + } + + return h.ToAPI(), nil } diff --git a/database/hook/get_webhook_test.go b/database/hook/get_webhook_test.go index 975c568af..37ab712ff 100644 --- a/database/hook/get_webhook_test.go +++ b/database/hook/get_webhook_test.go @@ -9,16 +9,43 @@ import ( "github.com/DATA-DOG/go-sqlmock" + api "github.com/go-vela/server/api/types" + "github.com/go-vela/server/constants" "github.com/go-vela/server/database/testutils" - "github.com/go-vela/types/library" ) func TestHook_Engine_GetHookByWebhookID(t *testing.T) { // setup types + _owner := testutils.APIUser().Crop() + _owner.SetID(1) + _owner.SetName("foo") + _owner.SetToken("bar") + + _repo := testutils.APIRepo() + _repo.SetID(1) + _repo.SetOwner(_owner) + _repo.SetHash("baz") + _repo.SetOrg("foo") + _repo.SetName("bar") + _repo.SetFullName("foo/bar") + _repo.SetVisibility("public") + _repo.SetAllowEvents(api.NewEventsFromMask(1)) + _repo.SetPipelineType(constants.PipelineTypeYAML) + _repo.SetTopics([]string{}) + + _repoBuild := new(api.Repo) + _repoBuild.SetID(1) + + _build := testutils.APIBuild() + _build.SetID(1) + _build.SetRepo(_repoBuild) + _build.SetNumber(1) + _build.SetDeployPayload(nil) + _hook := testutils.APIHook() _hook.SetID(1) - _hook.SetRepoID(1) - _hook.SetBuildID(1) + _hook.SetRepo(_repo) + _hook.SetBuild(_build) _hook.SetNumber(1) _hook.SetSourceID("c8da1302-07d6-11ea-882f-4893bca275b8") _hook.SetWebhookID(123456) @@ -31,23 +58,35 @@ func TestHook_Engine_GetHookByWebhookID(t *testing.T) { []string{"id", "repo_id", "build_id", "number", "source_id", "created", "host", "event", "event_action", "branch", "error", "status", "link", "webhook_id"}, ).AddRow(1, 1, 1, 1, "c8da1302-07d6-11ea-882f-4893bca275b8", 0, "", "", "", "", "", "", "", 123456) + _buildRows := sqlmock.NewRows( + []string{"id", "repo_id", "pipeline_id", "number", "parent", "event", "event_action", "status", "error", "enqueued", "created", "started", "finished", "deploy", "deploy_number", "deploy_payload", "clone", "source", "title", "message", "commit", "sender", "author", "email", "link", "branch", "ref", "base_ref", "head_ref", "host", "runtime", "distribution", "timestamp"}). + AddRow(1, 1, nil, 1, 0, "", "", "", "", 0, 0, 0, 0, "", 0, nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0) + + _repoRows := sqlmock.NewRows( + []string{"id", "user_id", "hash", "org", "name", "full_name", "link", "clone", "branch", "topics", "build_limit", "timeout", "counter", "visibility", "private", "trusted", "active", "allow_events", "pipeline_type", "previous_name", "approve_build"}). + AddRow(1, 1, "baz", "foo", "bar", "foo/bar", "", "", "", "{}", 0, 0, 0, "public", false, false, false, 1, "yaml", "", "") + + _userRows := sqlmock.NewRows( + []string{"id", "name", "token", "hash", "active", "admin"}). + AddRow(1, "foo", "bar", "baz", false, false) + // ensure the mock expects the query _mock.ExpectQuery(`SELECT * FROM "hooks" WHERE webhook_id = $1 LIMIT $2`).WithArgs(123456, 1).WillReturnRows(_rows) + _mock.ExpectQuery(`SELECT * FROM "builds" WHERE "builds"."id" = $1`).WithArgs(1).WillReturnRows(_buildRows) + _mock.ExpectQuery(`SELECT * FROM "repos" WHERE "repos"."id" = $1`).WithArgs(1).WillReturnRows(_repoRows) + _mock.ExpectQuery(`SELECT * FROM "users" WHERE "users"."id" = $1`).WithArgs(1).WillReturnRows(_userRows) _sqlite := testSqlite(t) defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() - _, err := _sqlite.CreateHook(context.TODO(), _hook) - if err != nil { - t.Errorf("unable to create test hook for sqlite: %v", err) - } + sqlitePopulateTables(t, _sqlite, []*api.Hook{_hook}, []*api.User{_owner}, []*api.Repo{_repo}, []*api.Build{_build}) // setup tests tests := []struct { failure bool name string database *engine - want *library.Hook + want *api.Hook }{ { failure: false, diff --git a/database/hook/hook.go b/database/hook/hook.go index 283dad00a..7d274d81a 100644 --- a/database/hook/hook.go +++ b/database/hook/hook.go @@ -9,12 +9,14 @@ import ( "github.com/sirupsen/logrus" "gorm.io/gorm" - "github.com/go-vela/types/constants" + "github.com/go-vela/server/constants" ) type ( // config represents the settings required to create the engine that implements the HookInterface interface. config struct { + // specifies the encryption key to use for the Hook engine + EncryptionKey string // specifies to skip creating tables and indexes for the Hook engine SkipCreation bool } diff --git a/database/hook/hook_test.go b/database/hook/hook_test.go index 615572398..0f632ea80 100644 --- a/database/hook/hook_test.go +++ b/database/hook/hook_test.go @@ -3,6 +3,7 @@ package hook import ( + "context" "reflect" "testing" @@ -11,6 +12,10 @@ import ( "gorm.io/driver/postgres" "gorm.io/driver/sqlite" "gorm.io/gorm" + + api "github.com/go-vela/server/api/types" + "github.com/go-vela/server/constants" + "github.com/go-vela/server/database/types" ) func TestHook_New(t *testing.T) { @@ -161,3 +166,49 @@ func testSqlite(t *testing.T) *engine { return _engine } + +// sqlitePopulateTables is a helper function to populate tables for testing. +func sqlitePopulateTables(t *testing.T, e *engine, hooks []*api.Hook, users []*api.User, repos []*api.Repo, builds []*api.Build) { + for _, _hook := range hooks { + _, err := e.CreateHook(context.TODO(), _hook) + if err != nil { + t.Errorf("unable to create test hook for sqlite: %v", err) + } + } + + err := e.client.AutoMigrate(&types.User{}) + if err != nil { + t.Errorf("unable to create user table for sqlite: %v", err) + } + + for _, _user := range users { + err = e.client.Table(constants.TableUser).Create(types.UserFromAPI(_user)).Error + if err != nil { + t.Errorf("unable to create test user for sqlite: %v", err) + } + } + + err = e.client.AutoMigrate(&types.Repo{}) + if err != nil { + t.Errorf("unable to create repo table for sqlite: %v", err) + } + + for _, _repo := range repos { + err = e.client.Table(constants.TableRepo).Create(types.RepoFromAPI(_repo)).Error + if err != nil { + t.Errorf("unable to create test repo for sqlite: %v", err) + } + } + + err = e.client.AutoMigrate(&types.Build{}) + if err != nil { + t.Errorf("unable to create build table for sqlite: %v", err) + } + + for _, _build := range builds { + err = e.client.Table(constants.TableBuild).Create(types.BuildFromAPI(_build)).Error + if err != nil { + t.Errorf("unable to create test build for sqlite: %v", err) + } + } +} diff --git a/database/hook/interface.go b/database/hook/interface.go index 674dca20c..aad449413 100644 --- a/database/hook/interface.go +++ b/database/hook/interface.go @@ -6,7 +6,6 @@ import ( "context" api "github.com/go-vela/server/api/types" - "github.com/go-vela/types/library" ) // HookInterface represents the Vela interface for hook @@ -32,21 +31,21 @@ type HookInterface interface { // CountHooksForRepo defines a function that gets the count of hooks by repo ID. CountHooksForRepo(context.Context, *api.Repo) (int64, error) // CreateHook defines a function that creates a new hook. - CreateHook(context.Context, *library.Hook) (*library.Hook, error) + CreateHook(context.Context, *api.Hook) (*api.Hook, error) // DeleteHook defines a function that deletes an existing hook. - DeleteHook(context.Context, *library.Hook) error + DeleteHook(context.Context, *api.Hook) error // GetHook defines a function that gets a hook by ID. - GetHook(context.Context, int64) (*library.Hook, error) + GetHook(context.Context, int64) (*api.Hook, error) // GetHookByWebhookID defines a function that gets any hook with a matching webhook_id. - GetHookByWebhookID(context.Context, int64) (*library.Hook, error) + GetHookByWebhookID(context.Context, int64) (*api.Hook, error) // GetHookForRepo defines a function that gets a hook by repo ID and number. - GetHookForRepo(context.Context, *api.Repo, int) (*library.Hook, error) + GetHookForRepo(context.Context, *api.Repo, int) (*api.Hook, error) // LastHookForRepo defines a function that gets the last hook by repo ID. - LastHookForRepo(context.Context, *api.Repo) (*library.Hook, error) + LastHookForRepo(context.Context, *api.Repo) (*api.Hook, error) // ListHooks defines a function that gets a list of all hooks. - ListHooks(context.Context) ([]*library.Hook, error) + ListHooks(context.Context) ([]*api.Hook, error) // ListHooksForRepo defines a function that gets a list of hooks by repo ID. - ListHooksForRepo(context.Context, *api.Repo, int, int) ([]*library.Hook, int64, error) + ListHooksForRepo(context.Context, *api.Repo, int, int) ([]*api.Hook, int64, error) // UpdateHook defines a function that updates an existing hook. - UpdateHook(context.Context, *library.Hook) (*library.Hook, error) + UpdateHook(context.Context, *api.Hook) (*api.Hook, error) } diff --git a/database/hook/last_repo.go b/database/hook/last_repo.go index 776f9b6c0..8299864fb 100644 --- a/database/hook/last_repo.go +++ b/database/hook/last_repo.go @@ -10,25 +10,27 @@ import ( "gorm.io/gorm" api "github.com/go-vela/server/api/types" + "github.com/go-vela/server/database/types" "github.com/go-vela/types/constants" - "github.com/go-vela/types/database" - "github.com/go-vela/types/library" ) // LastHookForRepo gets the last hook by repo ID from the database. -func (e *engine) LastHookForRepo(ctx context.Context, r *api.Repo) (*library.Hook, error) { +func (e *engine) LastHookForRepo(ctx context.Context, r *api.Repo) (*api.Hook, error) { e.logger.WithFields(logrus.Fields{ "org": r.GetOrg(), "repo": r.GetName(), }).Tracef("getting last hook for repo %s", r.GetFullName()) // variable to store query results - h := new(database.Hook) + h := new(types.Hook) // send query to the database and store result in variable err := e.client. WithContext(ctx). Table(constants.TableHook). + Preload("Repo"). + Preload("Repo.Owner"). + Preload("Build"). Where("repo_id = ?", r.GetID()). Order("number DESC"). Take(h). @@ -43,8 +45,13 @@ func (e *engine) LastHookForRepo(ctx context.Context, r *api.Repo) (*library.Hoo return nil, err } + err = h.Repo.Decrypt(e.config.EncryptionKey) + if err != nil { + e.logger.Errorf("unable to decrypt repo for hook %d: %v", h.ID.Int64, err) + } + // return the hook // // https://pkg.go.dev/github.com/go-vela/types/database#Hook.ToLibrary - return h.ToLibrary(), nil + return h.ToAPI(), nil } diff --git a/database/hook/last_repo_test.go b/database/hook/last_repo_test.go index 277932f2d..2e1bf1b80 100644 --- a/database/hook/last_repo_test.go +++ b/database/hook/last_repo_test.go @@ -9,26 +9,46 @@ import ( "github.com/DATA-DOG/go-sqlmock" + api "github.com/go-vela/server/api/types" + "github.com/go-vela/server/constants" "github.com/go-vela/server/database/testutils" - "github.com/go-vela/types/library" ) func TestHook_Engine_LastHookForRepo(t *testing.T) { // setup types - _hook := testutils.APIHook() - _hook.SetID(1) - _hook.SetRepoID(1) - _hook.SetBuildID(1) - _hook.SetNumber(1) - _hook.SetSourceID("c8da1302-07d6-11ea-882f-4893bca275b8") - _hook.SetWebhookID(1) + _owner := testutils.APIUser().Crop() + _owner.SetID(1) + _owner.SetName("foo") + _owner.SetToken("bar") _repo := testutils.APIRepo() _repo.SetID(1) - _repo.GetOwner().SetID(1) + _repo.SetOwner(_owner) + _repo.SetHash("baz") _repo.SetOrg("foo") _repo.SetName("bar") _repo.SetFullName("foo/bar") + _repo.SetVisibility("public") + _repo.SetAllowEvents(api.NewEventsFromMask(1)) + _repo.SetPipelineType(constants.PipelineTypeYAML) + _repo.SetTopics([]string{}) + + _repoBuild := new(api.Repo) + _repoBuild.SetID(1) + + _build := testutils.APIBuild() + _build.SetID(1) + _build.SetRepo(_repoBuild) + _build.SetNumber(1) + _build.SetDeployPayload(nil) + + _hook := testutils.APIHook() + _hook.SetID(1) + _hook.SetRepo(_repo) + _hook.SetBuild(_build) + _hook.SetNumber(1) + _hook.SetSourceID("c8da1302-07d6-11ea-882f-4893bca275b8") + _hook.SetWebhookID(1) _postgres, _mock := testPostgres(t) defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() @@ -38,23 +58,35 @@ func TestHook_Engine_LastHookForRepo(t *testing.T) { []string{"id", "repo_id", "build_id", "number", "source_id", "created", "host", "event", "event_action", "branch", "error", "status", "link", "webhook_id"}). AddRow(1, 1, 1, 1, "c8da1302-07d6-11ea-882f-4893bca275b8", 0, "", "", "", "", "", "", "", 1) + _buildRows := sqlmock.NewRows( + []string{"id", "repo_id", "pipeline_id", "number", "parent", "event", "event_action", "status", "error", "enqueued", "created", "started", "finished", "deploy", "deploy_number", "deploy_payload", "clone", "source", "title", "message", "commit", "sender", "author", "email", "link", "branch", "ref", "base_ref", "head_ref", "host", "runtime", "distribution", "timestamp"}). + AddRow(1, 1, nil, 1, 0, "", "", "", "", 0, 0, 0, 0, "", 0, nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0) + + _repoRows := sqlmock.NewRows( + []string{"id", "user_id", "hash", "org", "name", "full_name", "link", "clone", "branch", "topics", "build_limit", "timeout", "counter", "visibility", "private", "trusted", "active", "allow_events", "pipeline_type", "previous_name", "approve_build"}). + AddRow(1, 1, "baz", "foo", "bar", "foo/bar", "", "", "", "{}", 0, 0, 0, "public", false, false, false, 1, "yaml", "", "") + + _userRows := sqlmock.NewRows( + []string{"id", "name", "token", "hash", "active", "admin"}). + AddRow(1, "foo", "bar", "baz", false, false) + // ensure the mock expects the query _mock.ExpectQuery(`SELECT * FROM "hooks" WHERE repo_id = $1 ORDER BY number DESC LIMIT $2`).WithArgs(1, 1).WillReturnRows(_rows) + _mock.ExpectQuery(`SELECT * FROM "builds" WHERE "builds"."id" = $1`).WithArgs(1).WillReturnRows(_buildRows) + _mock.ExpectQuery(`SELECT * FROM "repos" WHERE "repos"."id" = $1`).WithArgs(1).WillReturnRows(_repoRows) + _mock.ExpectQuery(`SELECT * FROM "users" WHERE "users"."id" = $1`).WithArgs(1).WillReturnRows(_userRows) _sqlite := testSqlite(t) defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() - _, err := _sqlite.CreateHook(context.TODO(), _hook) - if err != nil { - t.Errorf("unable to create test hook for sqlite: %v", err) - } + sqlitePopulateTables(t, _sqlite, []*api.Hook{_hook}, []*api.User{_owner}, []*api.Repo{_repo}, []*api.Build{_build}) // setup tests tests := []struct { failure bool name string database *engine - want *library.Hook + want *api.Hook }{ { failure: false, diff --git a/database/hook/list.go b/database/hook/list.go index 3957f57f2..95da18ca6 100644 --- a/database/hook/list.go +++ b/database/hook/list.go @@ -5,19 +5,19 @@ package hook import ( "context" - "github.com/go-vela/types/constants" - "github.com/go-vela/types/database" - "github.com/go-vela/types/library" + api "github.com/go-vela/server/api/types" + "github.com/go-vela/server/constants" + "github.com/go-vela/server/database/types" ) // ListHooks gets a list of all hooks from the database. -func (e *engine) ListHooks(ctx context.Context) ([]*library.Hook, error) { +func (e *engine) ListHooks(ctx context.Context) ([]*api.Hook, error) { e.logger.Trace("listing all hooks") // variables to store query results and return value count := int64(0) - h := new([]database.Hook) - hooks := []*library.Hook{} + h := new([]types.Hook) + hooks := []*api.Hook{} // count the results count, err := e.CountHooks(ctx) @@ -34,6 +34,9 @@ func (e *engine) ListHooks(ctx context.Context) ([]*library.Hook, error) { err = e.client. WithContext(ctx). Table(constants.TableHook). + Preload("Repo"). + Preload("Repo.Owner"). + Preload("Build"). Find(&h). Error if err != nil { @@ -45,10 +48,12 @@ func (e *engine) ListHooks(ctx context.Context) ([]*library.Hook, error) { // https://golang.org/doc/faq#closures_and_goroutines tmp := hook - // convert query result to library type - // - // https://pkg.go.dev/github.com/go-vela/types/database#Hook.ToLibrary - hooks = append(hooks, tmp.ToLibrary()) + err = tmp.Repo.Decrypt(e.config.EncryptionKey) + if err != nil { + e.logger.Errorf("unable to decrypt repo for hook %d: %v", tmp.ID.Int64, err) + } + + hooks = append(hooks, tmp.ToAPI()) } return hooks, nil diff --git a/database/hook/list_repo.go b/database/hook/list_repo.go index 7d4487f63..7371ddcf5 100644 --- a/database/hook/list_repo.go +++ b/database/hook/list_repo.go @@ -8,13 +8,12 @@ import ( "github.com/sirupsen/logrus" api "github.com/go-vela/server/api/types" - "github.com/go-vela/types/constants" - "github.com/go-vela/types/database" - "github.com/go-vela/types/library" + "github.com/go-vela/server/constants" + "github.com/go-vela/server/database/types" ) // ListHooksForRepo gets a list of hooks by repo ID from the database. -func (e *engine) ListHooksForRepo(ctx context.Context, r *api.Repo, page, perPage int) ([]*library.Hook, int64, error) { +func (e *engine) ListHooksForRepo(ctx context.Context, r *api.Repo, page, perPage int) ([]*api.Hook, int64, error) { e.logger.WithFields(logrus.Fields{ "org": r.GetOrg(), "repo": r.GetName(), @@ -22,8 +21,8 @@ func (e *engine) ListHooksForRepo(ctx context.Context, r *api.Repo, page, perPag // variables to store query results and return value count := int64(0) - h := new([]database.Hook) - hooks := []*library.Hook{} + h := new([]types.Hook) + hooks := []*api.Hook{} // count the results count, err := e.CountHooksForRepo(ctx, r) @@ -43,6 +42,9 @@ func (e *engine) ListHooksForRepo(ctx context.Context, r *api.Repo, page, perPag err = e.client. WithContext(ctx). Table(constants.TableHook). + Preload("Repo"). + Preload("Repo.Owner"). + Preload("Build"). Where("repo_id = ?", r.GetID()). Order("id DESC"). Limit(perPage). @@ -58,10 +60,12 @@ func (e *engine) ListHooksForRepo(ctx context.Context, r *api.Repo, page, perPag // https://golang.org/doc/faq#closures_and_goroutines tmp := hook - // convert query result to library type - // - // https://pkg.go.dev/github.com/go-vela/types/database#Hook.ToLibrary - hooks = append(hooks, tmp.ToLibrary()) + err = tmp.Repo.Decrypt(e.config.EncryptionKey) + if err != nil { + e.logger.Errorf("unable to decrypt repo for hook %d: %v", tmp.ID.Int64, err) + } + + hooks = append(hooks, tmp.ToAPI()) } return hooks, count, nil diff --git a/database/hook/list_repo_test.go b/database/hook/list_repo_test.go index b68a41747..d14c6f375 100644 --- a/database/hook/list_repo_test.go +++ b/database/hook/list_repo_test.go @@ -4,40 +4,59 @@ package hook import ( "context" - "reflect" "testing" "github.com/DATA-DOG/go-sqlmock" + "github.com/google/go-cmp/cmp" + api "github.com/go-vela/server/api/types" + "github.com/go-vela/server/constants" "github.com/go-vela/server/database/testutils" - "github.com/go-vela/types/library" ) func TestHook_Engine_ListHooksForRepo(t *testing.T) { // setup types + _owner := testutils.APIUser().Crop() + _owner.SetID(1) + _owner.SetName("foo") + _owner.SetToken("bar") + + _repo := testutils.APIRepo() + _repo.SetID(1) + _repo.SetOwner(_owner) + _repo.SetHash("baz") + _repo.SetOrg("foo") + _repo.SetName("bar") + _repo.SetFullName("foo/bar") + _repo.SetVisibility("public") + _repo.SetAllowEvents(api.NewEventsFromMask(1)) + _repo.SetPipelineType(constants.PipelineTypeYAML) + _repo.SetTopics([]string{}) + + _repoBuild := new(api.Repo) + _repoBuild.SetID(1) + + _build := testutils.APIBuild() + _build.SetID(1) + _build.SetRepo(_repoBuild) + _build.SetNumber(1) + _build.SetDeployPayload(nil) + _hookOne := testutils.APIHook() _hookOne.SetID(1) - _hookOne.SetRepoID(1) - _hookOne.SetBuildID(1) + _hookOne.SetRepo(_repo) + _hookOne.SetBuild(_build) _hookOne.SetNumber(1) _hookOne.SetSourceID("c8da1302-07d6-11ea-882f-4893bca275b8") _hookOne.SetWebhookID(1) _hookTwo := testutils.APIHook() _hookTwo.SetID(2) - _hookTwo.SetRepoID(1) - _hookTwo.SetBuildID(2) + _hookTwo.SetRepo(_repo) _hookTwo.SetNumber(2) _hookTwo.SetSourceID("c8da1302-07d6-11ea-882f-4893bca275b8") _hookTwo.SetWebhookID(1) - _repo := testutils.APIRepo() - _repo.SetID(1) - _repo.GetOwner().SetID(1) - _repo.SetOrg("foo") - _repo.SetName("bar") - _repo.SetFullName("foo/bar") - _postgres, _mock := testPostgres(t) defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() @@ -50,43 +69,57 @@ func TestHook_Engine_ListHooksForRepo(t *testing.T) { // create expected result in mock _rows = sqlmock.NewRows( []string{"id", "repo_id", "build_id", "number", "source_id", "created", "host", "event", "event_action", "branch", "error", "status", "link", "webhook_id"}). - AddRow(2, 1, 2, 2, "c8da1302-07d6-11ea-882f-4893bca275b8", 0, "", "", "", "", "", "", "", 1). + AddRow(2, 1, 0, 2, "c8da1302-07d6-11ea-882f-4893bca275b8", 0, "", "", "", "", "", "", "", 1). AddRow(1, 1, 1, 1, "c8da1302-07d6-11ea-882f-4893bca275b8", 0, "", "", "", "", "", "", "", 1) + _buildRows := sqlmock.NewRows( + []string{"id", "repo_id", "pipeline_id", "number", "parent", "event", "event_action", "status", "error", "enqueued", "created", "started", "finished", "deploy", "deploy_number", "deploy_payload", "clone", "source", "title", "message", "commit", "sender", "author", "email", "link", "branch", "ref", "base_ref", "head_ref", "host", "runtime", "distribution", "timestamp"}). + AddRow(1, 1, nil, 1, 0, "", "", "", "", 0, 0, 0, 0, "", 0, nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0) + + _repoRows := sqlmock.NewRows( + []string{"id", "user_id", "hash", "org", "name", "full_name", "link", "clone", "branch", "topics", "build_limit", "timeout", "counter", "visibility", "private", "trusted", "active", "allow_events", "pipeline_type", "previous_name", "approve_build"}). + AddRow(1, 1, "baz", "foo", "bar", "foo/bar", "", "", "", "{}", 0, 0, 0, "public", false, false, false, 1, "yaml", "", "") + + _userRows := sqlmock.NewRows( + []string{"id", "name", "token", "hash", "active", "admin"}). + AddRow(1, "foo", "bar", "baz", false, false) + // ensure the mock expects the query _mock.ExpectQuery(`SELECT * FROM "hooks" WHERE repo_id = $1 ORDER BY id DESC LIMIT $2`).WithArgs(1, 10).WillReturnRows(_rows) + _mock.ExpectQuery(`SELECT * FROM "builds" WHERE "builds"."id" IN ($1,$2)`).WithArgs(0, 1).WillReturnRows(_buildRows) + _mock.ExpectQuery(`SELECT * FROM "repos" WHERE "repos"."id" = $1`).WithArgs(1).WillReturnRows(_repoRows) + _mock.ExpectQuery(`SELECT * FROM "users" WHERE "users"."id" = $1`).WithArgs(1).WillReturnRows(_userRows) _sqlite := testSqlite(t) defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() - _, err := _sqlite.CreateHook(context.TODO(), _hookOne) - if err != nil { - t.Errorf("unable to create test hook for sqlite: %v", err) - } - - _, err = _sqlite.CreateHook(context.TODO(), _hookTwo) - if err != nil { - t.Errorf("unable to create test hook for sqlite: %v", err) - } + sqlitePopulateTables( + t, + _sqlite, + []*api.Hook{_hookOne, _hookTwo}, + []*api.User{_owner}, + []*api.Repo{_repo}, + []*api.Build{_build}, + ) // setup tests tests := []struct { failure bool name string database *engine - want []*library.Hook + want []*api.Hook }{ { failure: false, name: "postgres", database: _postgres, - want: []*library.Hook{_hookTwo, _hookOne}, + want: []*api.Hook{_hookTwo, _hookOne}, }, { failure: false, name: "sqlite3", database: _sqlite, - want: []*library.Hook{_hookTwo, _hookOne}, + want: []*api.Hook{_hookTwo, _hookOne}, }, } @@ -95,6 +128,14 @@ func TestHook_Engine_ListHooksForRepo(t *testing.T) { t.Run(test.name, func(t *testing.T) { got, _, err := test.database.ListHooksForRepo(context.TODO(), _repo, 1, 10) + // empty values of build are different in testing but still empty + // TODO: fix complex types such as deploy payload, dashboards, favorites, etc for empty comps + for i, gotHook := range got { + if gotHook.GetBuild().GetID() == 0 { + gotHook.SetBuild(test.want[i].GetBuild()) + } + } + if test.failure { if err == nil { t.Errorf("ListHooksForRepo for %s should have returned err", test.name) @@ -107,8 +148,8 @@ func TestHook_Engine_ListHooksForRepo(t *testing.T) { t.Errorf("ListHooksForRepo for %s returned err: %v", test.name, err) } - if !reflect.DeepEqual(got, test.want) { - t.Errorf("ListHooksForRepo for %s is %v, want %v", test.name, got, test.want) + if diff := cmp.Diff(test.want, got); diff != "" { + t.Errorf("ListHooksForRepo for %s: -want, +got: %s", test.name, diff) } }) } diff --git a/database/hook/list_test.go b/database/hook/list_test.go index 89098751d..5d8aa4fe9 100644 --- a/database/hook/list_test.go +++ b/database/hook/list_test.go @@ -4,29 +4,55 @@ package hook import ( "context" - "reflect" "testing" "github.com/DATA-DOG/go-sqlmock" + "github.com/google/go-cmp/cmp" + api "github.com/go-vela/server/api/types" + "github.com/go-vela/server/constants" "github.com/go-vela/server/database/testutils" - "github.com/go-vela/types/library" ) func TestHook_Engine_ListHooks(t *testing.T) { // setup types + _owner := testutils.APIUser().Crop() + _owner.SetID(1) + _owner.SetName("foo") + _owner.SetToken("bar") + + _repo := testutils.APIRepo() + _repo.SetID(1) + _repo.SetOwner(_owner) + _repo.SetHash("baz") + _repo.SetOrg("foo") + _repo.SetName("bar") + _repo.SetFullName("foo/bar") + _repo.SetVisibility("public") + _repo.SetAllowEvents(api.NewEventsFromMask(1)) + _repo.SetPipelineType(constants.PipelineTypeYAML) + _repo.SetTopics([]string{}) + + _repoBuild := new(api.Repo) + _repoBuild.SetID(1) + + _build := testutils.APIBuild() + _build.SetID(1) + _build.SetRepo(_repoBuild) + _build.SetNumber(1) + _build.SetDeployPayload(nil) + _hookOne := testutils.APIHook() _hookOne.SetID(1) - _hookOne.SetRepoID(1) - _hookOne.SetBuildID(1) + _hookOne.SetRepo(_repo) + _hookOne.SetBuild(_build) _hookOne.SetNumber(1) _hookOne.SetSourceID("c8da1302-07d6-11ea-882f-4893bca275b8") _hookOne.SetWebhookID(1) _hookTwo := testutils.APIHook() _hookTwo.SetID(2) - _hookTwo.SetRepoID(1) - _hookTwo.SetBuildID(2) + _hookTwo.SetRepo(_repo) _hookTwo.SetNumber(2) _hookTwo.SetSourceID("c8da1302-07d6-11ea-882f-4893bca275b8") _hookTwo.SetWebhookID(1) @@ -44,42 +70,56 @@ func TestHook_Engine_ListHooks(t *testing.T) { _rows = sqlmock.NewRows( []string{"id", "repo_id", "build_id", "number", "source_id", "created", "host", "event", "event_action", "branch", "error", "status", "link", "webhook_id"}). AddRow(1, 1, 1, 1, "c8da1302-07d6-11ea-882f-4893bca275b8", 0, "", "", "", "", "", "", "", 1). - AddRow(2, 1, 2, 2, "c8da1302-07d6-11ea-882f-4893bca275b8", 0, "", "", "", "", "", "", "", 1) + AddRow(2, 1, 0, 2, "c8da1302-07d6-11ea-882f-4893bca275b8", 0, "", "", "", "", "", "", "", 1) + + _buildRows := sqlmock.NewRows( + []string{"id", "repo_id", "pipeline_id", "number", "parent", "event", "event_action", "status", "error", "enqueued", "created", "started", "finished", "deploy", "deploy_number", "deploy_payload", "clone", "source", "title", "message", "commit", "sender", "author", "email", "link", "branch", "ref", "base_ref", "head_ref", "host", "runtime", "distribution", "timestamp"}). + AddRow(1, 1, nil, 1, 0, "", "", "", "", 0, 0, 0, 0, "", 0, nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0) + + _repoRows := sqlmock.NewRows( + []string{"id", "user_id", "hash", "org", "name", "full_name", "link", "clone", "branch", "topics", "build_limit", "timeout", "counter", "visibility", "private", "trusted", "active", "allow_events", "pipeline_type", "previous_name", "approve_build"}). + AddRow(1, 1, "baz", "foo", "bar", "foo/bar", "", "", "", "{}", 0, 0, 0, "public", false, false, false, 1, "yaml", "", "") + + _userRows := sqlmock.NewRows( + []string{"id", "name", "token", "hash", "active", "admin"}). + AddRow(1, "foo", "bar", "baz", false, false) // ensure the mock expects the query _mock.ExpectQuery(`SELECT * FROM "hooks"`).WillReturnRows(_rows) + _mock.ExpectQuery(`SELECT * FROM "builds" WHERE "builds"."id" IN ($1,$2)`).WithArgs(1, 0).WillReturnRows(_buildRows) + _mock.ExpectQuery(`SELECT * FROM "repos" WHERE "repos"."id" = $1`).WithArgs(1).WillReturnRows(_repoRows) + _mock.ExpectQuery(`SELECT * FROM "users" WHERE "users"."id" = $1`).WithArgs(1).WillReturnRows(_userRows) _sqlite := testSqlite(t) defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() - _, err := _sqlite.CreateHook(context.TODO(), _hookOne) - if err != nil { - t.Errorf("unable to create test hook for sqlite: %v", err) - } - - _, err = _sqlite.CreateHook(context.TODO(), _hookTwo) - if err != nil { - t.Errorf("unable to create test hook for sqlite: %v", err) - } + sqlitePopulateTables( + t, + _sqlite, + []*api.Hook{_hookOne, _hookTwo}, + []*api.User{_owner}, + []*api.Repo{_repo}, + []*api.Build{_build}, + ) // setup tests tests := []struct { failure bool name string database *engine - want []*library.Hook + want []*api.Hook }{ { failure: false, name: "postgres", database: _postgres, - want: []*library.Hook{_hookOne, _hookTwo}, + want: []*api.Hook{_hookOne, _hookTwo}, }, { failure: false, name: "sqlite3", database: _sqlite, - want: []*library.Hook{_hookOne, _hookTwo}, + want: []*api.Hook{_hookOne, _hookTwo}, }, } @@ -88,6 +128,14 @@ func TestHook_Engine_ListHooks(t *testing.T) { t.Run(test.name, func(t *testing.T) { got, err := test.database.ListHooks(context.TODO()) + // empty values of build are different in testing but still empty + // TODO: fix complex types such as deploy payload, dashboards, favorites, etc for empty comps + for i, gotHook := range got { + if gotHook.GetBuild().GetID() == 0 { + gotHook.SetBuild(test.want[i].GetBuild()) + } + } + if test.failure { if err == nil { t.Errorf("ListHooks for %s should have returned err", test.name) @@ -100,8 +148,8 @@ func TestHook_Engine_ListHooks(t *testing.T) { t.Errorf("ListHooks for %s returned err: %v", test.name, err) } - if !reflect.DeepEqual(got, test.want) { - t.Errorf("ListHooks for %s is %v, want %v", test.name, got, test.want) + if diff := cmp.Diff(test.want, got); diff != "" { + t.Errorf("ListHooks for %s is a mismatch (-want +got):\n%s", test.name, diff) } }) } diff --git a/database/hook/opts.go b/database/hook/opts.go index 36b88b499..58304f058 100644 --- a/database/hook/opts.go +++ b/database/hook/opts.go @@ -32,6 +32,16 @@ func WithLogger(logger *logrus.Entry) EngineOpt { } } +// WithEncryptionKey sets the encryption key in the database engine for Builds. +func WithEncryptionKey(key string) EngineOpt { + return func(e *engine) error { + // set the encryption key in the build engine + e.config.EncryptionKey = key + + return nil + } +} + // WithSkipCreation sets the skip creation logic in the database engine for Hooks. func WithSkipCreation(skipCreation bool) EngineOpt { return func(e *engine) error { diff --git a/database/hook/table.go b/database/hook/table.go index a5d8d46ea..9d613b003 100644 --- a/database/hook/table.go +++ b/database/hook/table.go @@ -5,7 +5,7 @@ package hook import ( "context" - "github.com/go-vela/types/constants" + "github.com/go-vela/server/constants" ) const ( diff --git a/database/hook/update.go b/database/hook/update.go index bd6fe93ef..aec428053 100644 --- a/database/hook/update.go +++ b/database/hook/update.go @@ -7,21 +7,18 @@ import ( "github.com/sirupsen/logrus" - "github.com/go-vela/types/constants" - "github.com/go-vela/types/database" - "github.com/go-vela/types/library" + api "github.com/go-vela/server/api/types" + "github.com/go-vela/server/constants" + "github.com/go-vela/server/database/types" ) // UpdateHook updates an existing hook in the database. -func (e *engine) UpdateHook(ctx context.Context, h *library.Hook) (*library.Hook, error) { +func (e *engine) UpdateHook(ctx context.Context, h *api.Hook) (*api.Hook, error) { e.logger.WithFields(logrus.Fields{ "hook": h.GetNumber(), }).Tracef("updating hook %d", h.GetNumber()) - // cast the library type to database type - // - // https://pkg.go.dev/github.com/go-vela/types/database#HookFromLibrary - hook := database.HookFromLibrary(h) + hook := types.HookFromAPI(h) // validate the necessary fields are populated // @@ -31,11 +28,15 @@ func (e *engine) UpdateHook(ctx context.Context, h *library.Hook) (*library.Hook return nil, err } - result := e.client. - WithContext(ctx). - Table(constants.TableHook). - Save(hook) - // send query to the database - return hook.ToLibrary(), result.Error + err = e.client.WithContext(ctx).Table(constants.TableHook).Save(hook).Error + if err != nil { + return nil, err + } + + result := hook.ToAPI() + result.SetRepo(h.GetRepo()) + result.SetBuild(h.GetBuild()) + + return result, nil } diff --git a/database/hook/update_test.go b/database/hook/update_test.go index 72982b6c0..434156ceb 100644 --- a/database/hook/update_test.go +++ b/database/hook/update_test.go @@ -8,15 +8,22 @@ import ( "github.com/DATA-DOG/go-sqlmock" + api "github.com/go-vela/server/api/types" "github.com/go-vela/server/database/testutils" ) func TestHook_Engine_UpdateHook(t *testing.T) { // setup types + _repo := testutils.APIRepo() + _repo.SetID(1) + + _build := testutils.APIBuild() + _build.SetID(1) + _hook := testutils.APIHook() _hook.SetID(1) - _hook.SetRepoID(1) - _hook.SetBuildID(1) + _hook.SetRepo(_repo) + _hook.SetBuild(_build) _hook.SetNumber(1) _hook.SetSourceID("c8da1302-07d6-11ea-882f-4893bca275b8") _hook.SetWebhookID(1) @@ -34,10 +41,7 @@ WHERE "id" = $14`). _sqlite := testSqlite(t) defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() - _, err := _sqlite.CreateHook(context.TODO(), _hook) - if err != nil { - t.Errorf("unable to create test hook for sqlite: %v", err) - } + sqlitePopulateTables(t, _sqlite, []*api.Hook{_hook}, nil, nil, nil) // setup tests tests := []struct { @@ -60,7 +64,7 @@ WHERE "id" = $14`). // run tests for _, test := range tests { t.Run(test.name, func(t *testing.T) { - _, err = test.database.UpdateHook(context.TODO(), _hook) + _, err := test.database.UpdateHook(context.TODO(), _hook) if test.failure { if err == nil { diff --git a/database/integration_test.go b/database/integration_test.go index 465793800..d62464b3a 100644 --- a/database/integration_test.go +++ b/database/integration_test.go @@ -45,7 +45,7 @@ type Resources struct { Dashboards []*api.Dashboard Deployments []*library.Deployment Executables []*library.BuildExecutable - Hooks []*library.Hook + Hooks []*api.Hook JWKs jwk.Set Logs []*library.Log Pipelines []*library.Pipeline @@ -390,6 +390,8 @@ func testBuilds(t *testing.T, db Interface, resources *Resources) { // update the builds for _, build := range resources.Builds { + prevStatus := build.GetStatus() + build.SetStatus("success") _, err = db.UpdateBuild(context.TODO(), build) if err != nil { @@ -404,6 +406,8 @@ func testBuilds(t *testing.T, db Interface, resources *Resources) { if diff := cmp.Diff(build, got); diff != "" { t.Errorf("GetBuild() mismatch (-want +got):\n%s", diff) } + + build.SetStatus(prevStatus) } methods["UpdateBuild"] = true methods["GetBuild"] = true @@ -415,6 +419,7 @@ func testBuilds(t *testing.T, db Interface, resources *Resources) { t.Errorf("unable to delete build %d: %v", build.GetID(), err) } } + methods["DeleteBuild"] = true // delete the repos for build related functions @@ -566,6 +571,8 @@ func testExecutables(t *testing.T, db Interface, resources *Resources) { } methods["PopBuildExecutable"] = true + prevBuildStatus := resources.Builds[0].GetStatus() + resources.Builds[0].SetStatus(constants.StatusError) _, err := db.UpdateBuild(context.TODO(), resources.Builds[0]) @@ -573,6 +580,9 @@ func testExecutables(t *testing.T, db Interface, resources *Resources) { t.Errorf("unable to update build for clean executables test") } + // reset build status for other tests + resources.Builds[0].SetStatus(prevBuildStatus) + err = db.CreateBuildExecutable(context.TODO(), resources.Executables[0]) if err != nil { t.Errorf("unable to create executable %d: %v", resources.Executables[0].GetID(), err) @@ -594,6 +604,12 @@ func testExecutables(t *testing.T, db Interface, resources *Resources) { methods["CleanBuildExecutables"] = true + // remove build used for clean executables test + err = db.DeleteBuild(context.TODO(), resources.Builds[0]) + if err != nil { + t.Errorf("unable to delete build %d: %v", resources.Builds[0].GetID(), err) + } + // ensure we called all the methods we expected to for method, called := range methods { if !called { @@ -739,6 +755,30 @@ func testHooks(t *testing.T, db Interface, resources *Resources) { methods[element.Method(i).Name] = false } + // create the users for hook related functions (owners of repos) + for _, user := range resources.Users { + _, err := db.CreateUser(context.TODO(), user) + if err != nil { + t.Errorf("unable to create user %d: %v", user.GetID(), err) + } + } + + // create the repos for hook related functions + for _, repo := range resources.Repos { + _, err := db.CreateRepo(context.TODO(), repo) + if err != nil { + t.Errorf("unable to create repo %d: %v", repo.GetID(), err) + } + } + + // create the builds for hook related functions + for _, build := range resources.Builds { + _, err := db.CreateBuild(context.TODO(), build) + if err != nil { + t.Errorf("unable to create build %d: %v", build.GetID(), err) + } + } + // create the hooks for _, hook := range resources.Hooks { _, err := db.CreateHook(context.TODO(), hook) @@ -787,7 +827,7 @@ func testHooks(t *testing.T, db Interface, resources *Resources) { if int(count) != len(resources.Hooks)-1 { t.Errorf("ListHooksForRepo() is %v, want %v", count, len(resources.Hooks)) } - if diff := cmp.Diff([]*library.Hook{resources.Hooks[1], resources.Hooks[0]}, list); diff != "" { + if diff := cmp.Diff([]*api.Hook{resources.Hooks[2], resources.Hooks[0]}, list); diff != "" { t.Errorf("ListHooksForRepo() mismatch (-want +got):\n%s", diff) } methods["ListHooksForRepo"] = true @@ -797,7 +837,7 @@ func testHooks(t *testing.T, db Interface, resources *Resources) { if err != nil { t.Errorf("unable to get last hook for repo %d: %v", resources.Repos[0].GetID(), err) } - if diff := cmp.Diff(resources.Hooks[1], got); diff != "" { + if diff := cmp.Diff(resources.Hooks[2], got); diff != "" { t.Errorf("LastHookForRepo() mismatch (-want +got):\n%s", diff) } methods["LastHookForRepo"] = true @@ -814,10 +854,9 @@ func testHooks(t *testing.T, db Interface, resources *Resources) { // lookup the hooks by name for _, hook := range resources.Hooks { - repo := resources.Repos[hook.GetRepoID()-1] - got, err = db.GetHookForRepo(context.TODO(), repo, hook.GetNumber()) + got, err = db.GetHookForRepo(context.TODO(), hook.GetRepo(), hook.GetNumber()) if err != nil { - t.Errorf("unable to get hook %d for repo %d: %v", hook.GetID(), repo.GetID(), err) + t.Errorf("unable to get hook %d for repo %d: %v", hook.GetID(), hook.GetRepo().GetID(), err) } if diff := cmp.Diff(hook, got); diff != "" { t.Errorf("GetHookForRepo() mismatch (-want +got):\n%s", diff) @@ -854,6 +893,30 @@ func testHooks(t *testing.T, db Interface, resources *Resources) { } methods["DeleteHook"] = true + // delete the builds + for _, build := range resources.Builds { + err = db.DeleteBuild(context.TODO(), build) + if err != nil { + t.Errorf("unable to delete build: %v", err) + } + } + + // delete the repos for hook related functions + for _, repo := range resources.Repos { + err = db.DeleteRepo(context.TODO(), repo) + if err != nil { + t.Errorf("unable to delete repo: %v", err) + } + } + + // delete the users for the hook related functions + for _, user := range resources.Users { + err = db.DeleteUser(context.TODO(), user) + if err != nil { + t.Errorf("unable to delete user: %v", err) + } + } + // ensure we called all the methods we expected to for method, called := range methods { if !called { @@ -2526,10 +2589,16 @@ func newResources() *Resources { deploymentTwo.SetCreatedBy("octocat") deploymentTwo.SetBuilds(builds) - hookOne := new(library.Hook) + hookBuildOne := *buildOne + hookBuildOne.Repo = &api.Repo{ID: repoOne.ID} + + hookBuildTwo := *buildTwo + hookBuildTwo.Repo = &api.Repo{ID: repoOne.ID} + + hookOne := new(api.Hook) hookOne.SetID(1) - hookOne.SetRepoID(1) - hookOne.SetBuildID(1) + hookOne.SetRepo(repoOne) + hookOne.SetBuild(&hookBuildOne) hookOne.SetNumber(1) hookOne.SetSourceID("c8da1302-07d6-11ea-882f-4893bca275b8") hookOne.SetCreated(time.Now().UTC().Unix()) @@ -2542,10 +2611,9 @@ func newResources() *Resources { hookOne.SetLink("https://github.com/github/octocat/settings/hooks/1") hookOne.SetWebhookID(123456) - hookTwo := new(library.Hook) + hookTwo := new(api.Hook) hookTwo.SetID(2) - hookTwo.SetRepoID(1) - hookTwo.SetBuildID(1) + hookTwo.SetRepo(repoTwo) hookTwo.SetNumber(2) hookTwo.SetSourceID("c8da1302-07d6-11ea-882f-4893bca275b8") hookTwo.SetCreated(time.Now().UTC().Unix()) @@ -2558,11 +2626,11 @@ func newResources() *Resources { hookTwo.SetLink("https://github.com/github/octocat/settings/hooks/1") hookTwo.SetWebhookID(123456) - hookThree := new(library.Hook) + hookThree := new(api.Hook) hookThree.SetID(3) - hookThree.SetRepoID(2) - hookThree.SetBuildID(5) - hookThree.SetNumber(1) + hookThree.SetRepo(repoOne) + hookThree.SetBuild(&hookBuildTwo) + hookThree.SetNumber(3) hookThree.SetSourceID("c8da1302-07d6-11ea-882f-6793bca275b8") hookThree.SetCreated(time.Now().UTC().Unix()) hookThree.SetHost("github.com") @@ -2847,7 +2915,7 @@ func newResources() *Resources { Dashboards: []*api.Dashboard{dashboardOne, dashboardTwo}, Deployments: []*library.Deployment{deploymentOne, deploymentTwo}, Executables: []*library.BuildExecutable{executableOne, executableTwo}, - Hooks: []*library.Hook{hookOne, hookTwo, hookThree}, + Hooks: []*api.Hook{hookOne, hookTwo, hookThree}, JWKs: jwkSet, Logs: []*library.Log{logServiceOne, logServiceTwo, logStepOne, logStepTwo}, Pipelines: []*library.Pipeline{pipelineOne, pipelineTwo}, diff --git a/database/resource.go b/database/resource.go index c11341c64..d3705e571 100644 --- a/database/resource.go +++ b/database/resource.go @@ -91,6 +91,7 @@ func (e *engine) NewResources(ctx context.Context) error { hook.WithContext(e.ctx), hook.WithClient(e.client), hook.WithLogger(e.logger), + hook.WithEncryptionKey(e.config.EncryptionKey), hook.WithSkipCreation(e.config.SkipCreation), ) if err != nil { diff --git a/database/testutils/api_resources.go b/database/testutils/api_resources.go index 0831567ae..be1fa9d72 100644 --- a/database/testutils/api_resources.go +++ b/database/testutils/api_resources.go @@ -146,11 +146,11 @@ func APIUser() *api.User { } } -func APIHook() *library.Hook { - return &library.Hook{ +func APIHook() *api.Hook { + return &api.Hook{ ID: new(int64), - RepoID: new(int64), - BuildID: new(int64), + Repo: APIRepo(), + Build: APIBuild(), Number: new(int), SourceID: new(string), Created: new(int64), diff --git a/database/types/hook.go b/database/types/hook.go new file mode 100644 index 000000000..c76422535 --- /dev/null +++ b/database/types/hook.go @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: Apache-2.0 + +package types + +import ( + "database/sql" + "errors" + + api "github.com/go-vela/server/api/types" + "github.com/go-vela/server/util" +) + +var ( + // ErrEmptyHookNumber defines the error type when a + // Hook type has an empty Number field provided. + ErrEmptyHookNumber = errors.New("empty webhook number provided") + + // ErrEmptyHookRepoID defines the error type when a + // Hook type has an empty RepoID field provided. + ErrEmptyHookRepoID = errors.New("empty webhook repo_id provided") + + // ErrEmptyHookSourceID defines the error type when a + // Hook type has an empty SourceID field provided. + ErrEmptyHookSourceID = errors.New("empty webhook source_id provided") + + // ErrEmptyHookWebhookID defines the error type when a + // Hook type has an empty WebhookID field provided. + ErrEmptyHookWebhookID = errors.New("empty webhook webhook_id provided") +) + +// Hook is the database representation of a webhook for a repo. +type Hook struct { + ID sql.NullInt64 `sql:"id"` + RepoID sql.NullInt64 `sql:"repo_id"` + BuildID sql.NullInt64 `sql:"build_id"` + Number sql.NullInt64 `sql:"number"` + SourceID sql.NullString `sql:"source_id"` + Created sql.NullInt64 `sql:"created"` + Host sql.NullString `sql:"host"` + Event sql.NullString `sql:"event"` + EventAction sql.NullString `sql:"event_action"` + Branch sql.NullString `sql:"branch"` + Error sql.NullString `sql:"error"` + Status sql.NullString `sql:"status"` + Link sql.NullString `sql:"link"` + WebhookID sql.NullInt64 `sql:"webhook_id"` + + Repo Repo `gorm:"foreignKey:RepoID"` + Build Build `gorm:"foreignKey:BuildID"` +} + +// Nullify ensures the valid flag for +// the sql.Null types are properly set. +// +// When a field within the Hook type is the zero +// value for the field, the valid flag is set to +// false causing it to be NULL in the database. +func (h *Hook) Nullify() *Hook { + if h == nil { + return nil + } + + // check if the ID field should be false + if h.ID.Int64 == 0 { + h.ID.Valid = false + } + + // check if the RepoID field should be false + if h.RepoID.Int64 == 0 { + h.RepoID.Valid = false + } + + // check if the BuildID field should be false + if h.BuildID.Int64 == 0 { + h.BuildID.Valid = false + } + + // check if the Number field should be false + if h.Number.Int64 == 0 { + h.Number.Valid = false + } + + // check if the SourceID field should be false + if len(h.SourceID.String) == 0 { + h.SourceID.Valid = false + } + + // check if the Created field should be false + if h.Created.Int64 == 0 { + h.Created.Valid = false + } + + // check if the Host field should be false + if len(h.Host.String) == 0 { + h.Host.Valid = false + } + + // check if the Event field should be false + if len(h.Event.String) == 0 { + h.Event.Valid = false + } + + // check if the EventAction field should be false + if len(h.EventAction.String) == 0 { + h.EventAction.Valid = false + } + + // check if the Branch field should be false + if len(h.Branch.String) == 0 { + h.Branch.Valid = false + } + + // check if the Error field should be false + if len(h.Error.String) == 0 { + h.Error.Valid = false + } + + // check if the Status field should be false + if len(h.Status.String) == 0 { + h.Status.Valid = false + } + + // check if the Link field should be false + if len(h.Link.String) == 0 { + h.Link.Valid = false + } + + // check if the WebhookID field should be false + if h.WebhookID.Int64 == 0 { + h.WebhookID.Valid = false + } + + return h +} + +// ToAPI converts the Hook type +// to an API Hook type. +func (h *Hook) ToAPI() *api.Hook { + hook := new(api.Hook) + + // if there is a build, set the repo ID for the build and set the build in hook + build := h.Build.ToAPI() + if build.GetID() > 0 { + r := new(api.Repo) + r.SetID(h.RepoID.Int64) + + build.SetRepo(r) + + hook.SetBuild(build) + } + + hook.SetID(h.ID.Int64) + hook.SetRepo(h.Repo.ToAPI()) + hook.SetNumber(int(h.Number.Int64)) + hook.SetSourceID(h.SourceID.String) + hook.SetCreated(h.Created.Int64) + hook.SetHost(h.Host.String) + hook.SetEvent(h.Event.String) + hook.SetEventAction(h.EventAction.String) + hook.SetBranch(h.Branch.String) + hook.SetError(h.Error.String) + hook.SetStatus(h.Status.String) + hook.SetLink(h.Link.String) + hook.SetWebhookID(h.WebhookID.Int64) + + return hook +} + +// Validate verifies the necessary fields for +// the Hook type are populated correctly. +func (h *Hook) Validate() error { + // verify the RepoID field is populated + if h.RepoID.Int64 <= 0 { + return ErrEmptyHookRepoID + } + + // verify the Number field is populated + if h.Number.Int64 <= 0 { + return ErrEmptyHookNumber + } + + // verify the SourceID field is populated + if len(h.SourceID.String) <= 0 { + return ErrEmptyHookSourceID + } + + // verify the WebhookID field is populated + if h.WebhookID.Int64 <= 0 { + return ErrEmptyHookWebhookID + } + + // ensure that all Hook string fields + // that can be returned as JSON are sanitized + // to avoid unsafe HTML content + h.SourceID = sql.NullString{String: util.Sanitize(h.SourceID.String), Valid: h.SourceID.Valid} + h.Host = sql.NullString{String: util.Sanitize(h.Host.String), Valid: h.Host.Valid} + h.Event = sql.NullString{String: util.Sanitize(h.Event.String), Valid: h.Event.Valid} + h.EventAction = sql.NullString{String: util.Sanitize(h.EventAction.String), Valid: h.EventAction.Valid} + h.Branch = sql.NullString{String: util.Sanitize(h.Branch.String), Valid: h.Branch.Valid} + h.Error = sql.NullString{String: util.Sanitize(h.Error.String), Valid: h.Error.Valid} + h.Status = sql.NullString{String: util.Sanitize(h.Status.String), Valid: h.Status.Valid} + h.Link = sql.NullString{String: util.Sanitize(h.Link.String), Valid: h.Link.Valid} + + return nil +} + +// HookFromAPI converts the API Hook type +// to a database Hook type. +func HookFromAPI(h *api.Hook) *Hook { + hook := &Hook{ + ID: sql.NullInt64{Int64: h.GetID(), Valid: true}, + RepoID: sql.NullInt64{Int64: h.GetRepo().GetID(), Valid: true}, + BuildID: sql.NullInt64{Int64: h.GetBuild().GetID(), Valid: true}, + Number: sql.NullInt64{Int64: int64(h.GetNumber()), Valid: true}, + SourceID: sql.NullString{String: h.GetSourceID(), Valid: true}, + Created: sql.NullInt64{Int64: h.GetCreated(), Valid: true}, + Host: sql.NullString{String: h.GetHost(), Valid: true}, + Event: sql.NullString{String: h.GetEvent(), Valid: true}, + EventAction: sql.NullString{String: h.GetEventAction(), Valid: true}, + Branch: sql.NullString{String: h.GetBranch(), Valid: true}, + Error: sql.NullString{String: h.GetError(), Valid: true}, + Status: sql.NullString{String: h.GetStatus(), Valid: true}, + Link: sql.NullString{String: h.GetLink(), Valid: true}, + WebhookID: sql.NullInt64{Int64: h.GetWebhookID(), Valid: true}, + } + + return hook.Nullify() +} diff --git a/database/types/hook_test.go b/database/types/hook_test.go new file mode 100644 index 000000000..e9b16dcea --- /dev/null +++ b/database/types/hook_test.go @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: Apache-2.0 + +package types + +import ( + "database/sql" + "reflect" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + + api "github.com/go-vela/server/api/types" +) + +func TestTypes_Hook_Nullify(t *testing.T) { + // setup types + var h *Hook + + want := &Hook{ + ID: sql.NullInt64{Int64: 0, Valid: false}, + RepoID: sql.NullInt64{Int64: 0, Valid: false}, + BuildID: sql.NullInt64{Int64: 0, Valid: false}, + Number: sql.NullInt64{Int64: 0, Valid: false}, + SourceID: sql.NullString{String: "", Valid: false}, + Created: sql.NullInt64{Int64: 0, Valid: false}, + Host: sql.NullString{String: "", Valid: false}, + Event: sql.NullString{String: "", Valid: false}, + EventAction: sql.NullString{String: "", Valid: false}, + Branch: sql.NullString{String: "", Valid: false}, + Error: sql.NullString{String: "", Valid: false}, + Status: sql.NullString{String: "", Valid: false}, + Link: sql.NullString{String: "", Valid: false}, + WebhookID: sql.NullInt64{Int64: 0, Valid: false}, + } + + // setup tests + tests := []struct { + hook *Hook + want *Hook + }{ + { + hook: testHook(), + want: testHook(), + }, + { + hook: h, + want: nil, + }, + { + hook: new(Hook), + want: want, + }, + } + + // run tests + for _, test := range tests { + got := test.hook.Nullify() + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("Nullify is %v, want %v", got, test.want) + } + } +} + +func TestTypes_Hook_ToAPI(t *testing.T) { + // setup types + want := new(api.Hook) + want.SetID(1) + want.SetRepo(testRepo().ToAPI()) + want.SetNumber(1) + want.SetSourceID("c8da1302-07d6-11ea-882f-4893bca275b8") + want.SetCreated(time.Now().UTC().Unix()) + want.SetHost("github.com") + want.SetEvent("push") + want.SetEventAction("") + want.SetBranch("main") + want.SetError("") + want.SetStatus("success") + want.SetLink("https://github.com/github/octocat/settings/hooks/1") + want.SetWebhookID(123456) + + wantBuild := *testBuild().ToAPI() + wantBuild.Repo = &api.Repo{ID: want.GetRepo().ID} + + want.SetBuild(&wantBuild) + + // run test + got := testHook().ToAPI() + + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("ToAPI() mismatch (-want +got):\n%s", diff) + } +} + +func TestTypes_Hook_Validate(t *testing.T) { + // setup tests + tests := []struct { + failure bool + hook *Hook + }{ + { + failure: false, + hook: testHook(), + }, + { // no number set for hook + failure: true, + hook: &Hook{ + ID: sql.NullInt64{Int64: 1, Valid: true}, + RepoID: sql.NullInt64{Int64: 1, Valid: true}, + SourceID: sql.NullString{String: "c8da1302-07d6-11ea-882f-4893bca275b8", Valid: true}, + WebhookID: sql.NullInt64{Int64: 1, Valid: true}, + }, + }, + { // no repo_id set for hook + failure: true, + hook: &Hook{ + ID: sql.NullInt64{Int64: 1, Valid: true}, + Number: sql.NullInt64{Int64: 1, Valid: true}, + SourceID: sql.NullString{String: "c8da1302-07d6-11ea-882f-4893bca275b8", Valid: true}, + WebhookID: sql.NullInt64{Int64: 1, Valid: true}, + }, + }, + { // no source_id set for hook + failure: true, + hook: &Hook{ + ID: sql.NullInt64{Int64: 1, Valid: true}, + Number: sql.NullInt64{Int64: 1, Valid: true}, + RepoID: sql.NullInt64{Int64: 1, Valid: true}, + WebhookID: sql.NullInt64{Int64: 1, Valid: true}, + }, + }, + { // no webhook_id set for hook + failure: true, + hook: &Hook{ + ID: sql.NullInt64{Int64: 1, Valid: true}, + Number: sql.NullInt64{Int64: 1, Valid: true}, + RepoID: sql.NullInt64{Int64: 1, Valid: true}, + SourceID: sql.NullString{String: "c8da1302-07d6-11ea-882f-4893bca275b8", Valid: true}, + }, + }, + } + + // run tests + for _, test := range tests { + err := test.hook.Validate() + + if test.failure { + if err == nil { + t.Errorf("Validate should have returned err") + } + + continue + } + + if err != nil { + t.Errorf("Validate returned err: %v", err) + } + } +} + +func TestTypes_HookFromAPI(t *testing.T) { + // setup types + want := &Hook{ + ID: sql.NullInt64{Int64: 1, Valid: true}, + RepoID: sql.NullInt64{Int64: 1, Valid: true}, + BuildID: sql.NullInt64{Int64: 1, Valid: true}, + Number: sql.NullInt64{Int64: 1, Valid: true}, + SourceID: sql.NullString{String: "c8da1302-07d6-11ea-882f-4893bca275b8", Valid: true}, + Created: sql.NullInt64{Int64: time.Now().UTC().Unix(), Valid: true}, + Host: sql.NullString{String: "github.com", Valid: true}, + Event: sql.NullString{String: "pull_request", Valid: true}, + EventAction: sql.NullString{String: "opened", Valid: true}, + Branch: sql.NullString{String: "main", Valid: true}, + Error: sql.NullString{String: "", Valid: false}, + Status: sql.NullString{String: "success", Valid: true}, + Link: sql.NullString{String: "https://github.com/github/octocat/settings/hooks/1", Valid: true}, + WebhookID: sql.NullInt64{Int64: 123456, Valid: true}, + } + + h := new(api.Hook) + h.SetID(1) + h.SetRepo(testRepo().ToAPI()) + h.SetBuild(testBuild().ToAPI()) + h.SetNumber(1) + h.SetSourceID("c8da1302-07d6-11ea-882f-4893bca275b8") + h.SetCreated(time.Now().UTC().Unix()) + h.SetHost("github.com") + h.SetEvent("pull_request") + h.SetEventAction("opened") + h.SetBranch("main") + h.SetError("") + h.SetStatus("success") + h.SetLink("https://github.com/github/octocat/settings/hooks/1") + h.SetWebhookID(123456) + + // run test + got := HookFromAPI(h) + + if !reflect.DeepEqual(got, want) { + t.Errorf("HookFromAPI is %v, want %v", got, want) + } +} + +// testHook is a test helper function to create a Hook +// type with all fields set to a fake value. +func testHook() *Hook { + return &Hook{ + ID: sql.NullInt64{Int64: 1, Valid: true}, + RepoID: sql.NullInt64{Int64: 1, Valid: true}, + BuildID: sql.NullInt64{Int64: 1, Valid: true}, + Number: sql.NullInt64{Int64: 1, Valid: true}, + SourceID: sql.NullString{String: "c8da1302-07d6-11ea-882f-4893bca275b8", Valid: true}, + Created: sql.NullInt64{Int64: time.Now().UTC().Unix(), Valid: true}, + Host: sql.NullString{String: "github.com", Valid: true}, + Event: sql.NullString{String: "push", Valid: true}, + EventAction: sql.NullString{String: "", Valid: false}, + Branch: sql.NullString{String: "main", Valid: true}, + Error: sql.NullString{String: "", Valid: false}, + Status: sql.NullString{String: "success", Valid: true}, + Link: sql.NullString{String: "https://github.com/github/octocat/settings/hooks/1", Valid: true}, + WebhookID: sql.NullInt64{Int64: 123456, Valid: true}, + + Repo: *testRepo(), + Build: *testBuild(), + } +} diff --git a/internal/webhook.go b/internal/webhook.go index 9c193fe58..4e38ab185 100644 --- a/internal/webhook.go +++ b/internal/webhook.go @@ -27,7 +27,7 @@ type PullRequest struct { // the required data when processing webhook event // a for a source provider event. type Webhook struct { - Hook *library.Hook + Hook *api.Hook Repo *api.Repo Build *api.Build PullRequest PullRequest diff --git a/mock/server/hook.go b/mock/server/hook.go index 58078da99..84334bd42 100644 --- a/mock/server/hook.go +++ b/mock/server/hook.go @@ -11,16 +11,99 @@ import ( "github.com/gin-gonic/gin" + api "github.com/go-vela/server/api/types" "github.com/go-vela/types" - "github.com/go-vela/types/library" ) const ( // HookResp represents a JSON return for a single hook. HookResp = `{ "id": 1, - "repo_id": 1, - "build_id": 1, + "repo": { + "id": 1, + "owner": { + "id": 1, + "name": "octocat", + "favorites": [], + "active": true, + "admin": false + }, + "org": "github", + "counter": 10, + "name": "octocat", + "full_name": "github/octocat", + "link": "https://github.com/github/octocat", + "clone": "https://github.com/github/octocat", + "branch": "main", + "build_limit": 10, + "timeout": 60, + "visibility": "public", + "private": false, + "trusted": true, + "pipeline_type": "yaml", + "topics": [], + "active": true, + "allow_events": { + "push": { + "branch": true, + "tag": true + }, + "pull_request": { + "opened": true, + "synchronize": true, + "reopened": true, + "edited": false + }, + "deployment": { + "created": true + }, + "comment": { + "created": false, + "edited": false + } + }, + "approve_build": "fork-always", + "previous_name": "" + }, + "build": { + "id": 1, + "repo": { + "id": 1 + }, + "pipeline_id": 1, + "number": 1, + "parent": 1, + "event": "push", + "event_action": "", + "status": "created", + "error": "", + "enqueued": 1563474077, + "created": 1563474076, + "started": 1563474077, + "finished": 0, + "deploy": "", + "deploy_number": 1, + "deploy_payload": {}, + "clone": "https://github.com/github/octocat.git", + "source": "https://github.com/github/octocat/commit/48afb5bdc41ad69bf22588491333f7cf71135163", + "title": "push received from https://github.com/github/octocat", + "message": "First commit...", + "commit": "48afb5bdc41ad69bf22588491333f7cf71135163", + "sender": "OctoKitty", + "sender_scm_id": "0", + "author": "OctoKitty", + "email": "octokitty@github.com", + "link": "https://vela.example.company.com/github/octocat/1", + "branch": "main", + "ref": "refs/heads/main", + "head_ref": "", + "base_ref": "", + "host": "example.company.com", + "runtime": "docker", + "distribution": "linux", + "approved_at": 0, + "approved_by": "" +}, "number": 1, "source_id": "c8da1302-07d6-11ea-882f-4893bca275b8", "created": 1563475419, @@ -36,24 +119,153 @@ const ( // HooksResp represents a JSON return for one to many hooks. HooksResp = `[ - { - "id": 2, - "repo_id": 1, - "build_id": 1, - "number": 2, - "source_id": "c8da1302-07d6-11ea-882f-4893bca275b8", - "created": 1563475420, - "host": "github.com", - "event": "push", - "branch": "main", - "error": "", - "status": "success", - "link": "https://github.com/github/octocat/settings/hooks/1" - }, +{ + "id": 1, + "repo": { + "id": 1, + "owner": { + "id": 1, + "name": "octocat", + "favorites": [], + "active": true, + "admin": false + }, + "org": "github", + "counter": 10, + "name": "octocat", + "full_name": "github/octocat", + "link": "https://github.com/github/octocat", + "clone": "https://github.com/github/octocat", + "branch": "main", + "build_limit": 10, + "timeout": 60, + "visibility": "public", + "private": false, + "trusted": true, + "pipeline_type": "yaml", + "topics": [], + "active": true, + "allow_events": { + "push": { + "branch": true, + "tag": true + }, + "pull_request": { + "opened": true, + "synchronize": true, + "reopened": true, + "edited": false + }, + "deployment": { + "created": true + }, + "comment": { + "created": false, + "edited": false + } + }, + "approve_build": "fork-always", + "previous_name": "" + }, + "build": { + "id": 1, + "repo": { + "id": 1 + }, + "pipeline_id": 1, + "number": 1, + "parent": 1, + "event": "push", + "event_action": "", + "status": "created", + "error": "", + "enqueued": 1563474077, + "created": 1563474076, + "started": 1563474077, + "finished": 0, + "deploy": "", + "deploy_number": 1, + "deploy_payload": {}, + "clone": "https://github.com/github/octocat.git", + "source": "https://github.com/github/octocat/commit/48afb5bdc41ad69bf22588491333f7cf71135163", + "title": "push received from https://github.com/github/octocat", + "message": "First commit...", + "commit": "48afb5bdc41ad69bf22588491333f7cf71135163", + "sender": "OctoKitty", + "sender_scm_id": "0", + "author": "OctoKitty", + "email": "octokitty@github.com", + "link": "https://vela.example.company.com/github/octocat/1", + "branch": "main", + "ref": "refs/heads/main", + "head_ref": "", + "base_ref": "", + "host": "example.company.com", + "runtime": "docker", + "distribution": "linux", + "approved_at": 0, + "approved_by": "" +}, + "number": 1, + "source_id": "c8da1302-07d6-11ea-882f-4893bca275b8", + "created": 1563475419, + "host": "github.com", + "event": "push", + "event_action": "", + "webhook_id": 1234, + "branch": "main", + "error": "", + "status": "success", + "link": "https://github.com/github/octocat/settings/hooks/1" +}, { "id": 1, - "repo_id": 1, - "build_id": 1, + "repo": { + "id": 1, + "owner": { + "id": 1, + "name": "octocat", + "favorites": [], + "active": true, + "admin": false + }, + "org": "github", + "counter": 10, + "name": "octocat", + "full_name": "github/octocat", + "link": "https://github.com/github/octocat", + "clone": "https://github.com/github/octocat", + "branch": "main", + "build_limit": 10, + "timeout": 60, + "visibility": "public", + "private": false, + "trusted": true, + "pipeline_type": "yaml", + "topics": [], + "active": true, + "allow_events": { + "push": { + "branch": true, + "tag": true + }, + "pull_request": { + "opened": true, + "synchronize": true, + "reopened": true, + "edited": false + }, + "deployment": { + "created": true + }, + "comment": { + "created": false, + "edited": false + } + }, + "approve_build": "fork-always", + "previous_name": "" + }, "number": 1, "source_id": "c8da1302-07d6-11ea-882f-4893bca275b8", "created": 1563475419, @@ -71,7 +283,7 @@ const ( func getHooks(c *gin.Context) { data := []byte(HooksResp) - var body []library.Hook + var body []api.Hook _ = json.Unmarshal(data, &body) c.JSON(http.StatusOK, body) @@ -93,7 +305,7 @@ func getHook(c *gin.Context) { data := []byte(HookResp) - var body library.Hook + var body api.Hook _ = json.Unmarshal(data, &body) c.JSON(http.StatusOK, body) @@ -103,7 +315,7 @@ func getHook(c *gin.Context) { func addHook(c *gin.Context) { data := []byte(HookResp) - var body library.Hook + var body api.Hook _ = json.Unmarshal(data, &body) c.JSON(http.StatusCreated, body) @@ -127,7 +339,7 @@ func updateHook(c *gin.Context) { data := []byte(HookResp) - var body library.Hook + var body api.Hook _ = json.Unmarshal(data, &body) c.JSON(http.StatusOK, body) diff --git a/mock/server/hook_test.go b/mock/server/hook_test.go index 9a346fa33..8ce8af220 100644 --- a/mock/server/hook_test.go +++ b/mock/server/hook_test.go @@ -7,11 +7,11 @@ import ( "reflect" "testing" - "github.com/go-vela/types/library" + api "github.com/go-vela/server/api/types" ) func TestHook_ActiveHookResp(t *testing.T) { - testHook := library.Hook{} + testHook := api.Hook{} err := json.Unmarshal([]byte(HookResp), &testHook) if err != nil { diff --git a/router/middleware/hook/context.go b/router/middleware/hook/context.go index b1fa95a4c..640969321 100644 --- a/router/middleware/hook/context.go +++ b/router/middleware/hook/context.go @@ -5,7 +5,7 @@ package hook import ( "context" - "github.com/go-vela/types/library" + api "github.com/go-vela/server/api/types" ) const key = "hook" @@ -15,14 +15,14 @@ type Setter interface { Set(string, interface{}) } -// FromContext returns the Repo associated with this context. -func FromContext(c context.Context) *library.Hook { +// FromContext returns the hook associated with this context. +func FromContext(c context.Context) *api.Hook { value := c.Value(key) if value == nil { return nil } - r, ok := value.(*library.Hook) + r, ok := value.(*api.Hook) if !ok { return nil } @@ -30,8 +30,8 @@ func FromContext(c context.Context) *library.Hook { return r } -// ToContext adds the Repo to this context if it supports +// ToContext adds the hook to this context if it supports // the Setter interface. -func ToContext(c Setter, r *library.Hook) { - c.Set(key, r) +func ToContext(c Setter, h *api.Hook) { + c.Set(key, h) } diff --git a/router/middleware/hook/context_test.go b/router/middleware/hook/context_test.go index 316632435..fc55d6592 100644 --- a/router/middleware/hook/context_test.go +++ b/router/middleware/hook/context_test.go @@ -7,13 +7,13 @@ import ( "github.com/gin-gonic/gin" - "github.com/go-vela/types/library" + api "github.com/go-vela/server/api/types" ) func TestHook_FromContext(t *testing.T) { // setup types num := int64(1) - want := &library.Hook{ID: &num} + want := &api.Hook{ID: &num} // setup context gin.SetMode(gin.TestMode) @@ -72,7 +72,7 @@ func TestHook_FromContext_Empty(t *testing.T) { func TestHook_ToContext(t *testing.T) { // setup types num := int64(1) - want := &library.Hook{ID: &num} + want := &api.Hook{ID: &num} // setup context gin.SetMode(gin.TestMode) diff --git a/router/middleware/hook/hook.go b/router/middleware/hook/hook.go index 18d0c7c21..554a2572f 100644 --- a/router/middleware/hook/hook.go +++ b/router/middleware/hook/hook.go @@ -10,15 +10,15 @@ import ( "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" + api "github.com/go-vela/server/api/types" "github.com/go-vela/server/database" "github.com/go-vela/server/router/middleware/org" "github.com/go-vela/server/router/middleware/repo" "github.com/go-vela/server/util" - "github.com/go-vela/types/library" ) // Retrieve gets the hook in the given context. -func Retrieve(c *gin.Context) *library.Hook { +func Retrieve(c *gin.Context) *api.Hook { return FromContext(c) } diff --git a/router/middleware/hook/hook_test.go b/router/middleware/hook/hook_test.go index fa3deef79..dc4316215 100644 --- a/router/middleware/hook/hook_test.go +++ b/router/middleware/hook/hook_test.go @@ -6,22 +6,22 @@ import ( "context" "net/http" "net/http/httptest" - "reflect" "testing" "github.com/gin-gonic/gin" + "github.com/google/go-cmp/cmp" "github.com/sirupsen/logrus" api "github.com/go-vela/server/api/types" "github.com/go-vela/server/database" + "github.com/go-vela/server/database/testutils" "github.com/go-vela/server/router/middleware/org" "github.com/go-vela/server/router/middleware/repo" - "github.com/go-vela/types/library" ) func TestHook_Retrieve(t *testing.T) { // setup types - want := new(library.Hook) + want := new(api.Hook) want.SetID(1) // setup context @@ -39,10 +39,12 @@ func TestHook_Retrieve(t *testing.T) { func TestHook_Establish(t *testing.T) { // setup types - owner := new(api.User) + owner := testutils.APIUser().Crop() owner.SetID(1) + owner.SetName("octocat") + owner.SetToken("foo") - r := new(api.Repo) + r := testutils.APIRepo() r.SetID(1) r.SetOwner(owner) r.SetHash("baz") @@ -51,10 +53,9 @@ func TestHook_Establish(t *testing.T) { r.SetFullName("foo/bar") r.SetVisibility("public") - want := new(library.Hook) + want := new(api.Hook) want.SetID(1) - want.SetRepoID(1) - want.SetBuildID(0) + want.SetRepo(r) want.SetNumber(1) want.SetSourceID("ok") want.SetStatus("") @@ -69,7 +70,7 @@ func TestHook_Establish(t *testing.T) { want.SetLink("") want.SetWebhookID(1) - got := new(library.Hook) + got := new(api.Hook) // setup database db, err := database.NewTest() @@ -78,13 +79,26 @@ func TestHook_Establish(t *testing.T) { } defer func() { + _ = db.DeleteUser(context.TODO(), owner) _ = db.DeleteRepo(context.TODO(), r) _ = db.DeleteHook(context.TODO(), want) db.Close() }() - _, _ = db.CreateRepo(context.TODO(), r) - _, _ = db.CreateHook(context.TODO(), want) + _, err = db.CreateUser(context.TODO(), owner) + if err != nil { + t.Errorf("unable to create test user: %v", err) + } + + _, err = db.CreateRepo(context.TODO(), r) + if err != nil { + t.Errorf("unable to create test repository: %v", err) + } + + _, err = db.CreateHook(context.TODO(), want) + if err != nil { + t.Errorf("unable to create test hook: %v", err) + } // setup context gin.SetMode(gin.TestMode) @@ -112,8 +126,8 @@ func TestHook_Establish(t *testing.T) { t.Errorf("Establish returned %v, want %v", resp.Code, http.StatusOK) } - if !reflect.DeepEqual(got, want) { - t.Errorf("Establish is %v, want %v", got, want) + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("Establish mismatch (-want +got):\n%s", diff) } } diff --git a/scm/github/repo.go b/scm/github/repo.go index 395c5e375..8e4a92ed8 100644 --- a/scm/github/repo.go +++ b/scm/github/repo.go @@ -150,7 +150,7 @@ func (c *client) Disable(ctx context.Context, u *api.User, org, name string) err } // Enable activates a repo by creating the webhook. -func (c *client) Enable(ctx context.Context, u *api.User, r *api.Repo, h *library.Hook) (*library.Hook, string, error) { +func (c *client) Enable(ctx context.Context, u *api.User, r *api.Repo, h *api.Hook) (*api.Hook, string, error) { c.Logger.WithFields(logrus.Fields{ "org": r.GetOrg(), "repo": r.GetName(), @@ -202,7 +202,7 @@ func (c *client) Enable(ctx context.Context, u *api.User, r *api.Repo, h *librar hookInfo, resp, err := client.Repositories.CreateHook(ctx, r.GetOrg(), r.GetName(), hook) // create the first hook for the repo and record its ID from GitHub - webhook := new(library.Hook) + webhook := new(api.Hook) webhook.SetWebhookID(hookInfo.GetID()) webhook.SetSourceID(r.GetName() + "-" + eventInitialize) webhook.SetCreated(hookInfo.GetCreatedAt().Unix()) diff --git a/scm/github/repo_test.go b/scm/github/repo_test.go index 7ef874181..a1920e73d 100644 --- a/scm/github/repo_test.go +++ b/scm/github/repo_test.go @@ -678,7 +678,7 @@ func TestGithub_Enable(t *testing.T) { u.SetName("foo") u.SetToken("bar") - wantHook := new(library.Hook) + wantHook := new(api.Hook) wantHook.SetWebhookID(1) wantHook.SetSourceID("bar-initialize") wantHook.SetCreated(1315329987) @@ -695,7 +695,7 @@ func TestGithub_Enable(t *testing.T) { client, _ := NewTest(s.URL) // run test - got, _, err := client.Enable(context.TODO(), u, r, new(library.Hook)) + got, _, err := client.Enable(context.TODO(), u, r, new(api.Hook)) if resp.Code != http.StatusOK { t.Errorf("Enable returned %v, want %v", resp.Code, http.StatusOK) diff --git a/scm/github/webhook.go b/scm/github/webhook.go index e2cd4359b..c6a3205a6 100644 --- a/scm/github/webhook.go +++ b/scm/github/webhook.go @@ -29,7 +29,7 @@ func (c *client) ProcessWebhook(ctx context.Context, request *http.Request) (*in c.Logger.Tracef("processing GitHub webhook") // create our own record of the hook and populate its fields - h := new(library.Hook) + h := new(api.Hook) h.SetNumber(1) h.SetSourceID(request.Header.Get("X-GitHub-Delivery")) @@ -99,18 +99,23 @@ func (c *client) VerifyWebhook(ctx context.Context, request *http.Request, r *ap } // RedeliverWebhook redelivers webhooks from GitHub. -func (c *client) RedeliverWebhook(ctx context.Context, u *api.User, r *api.Repo, h *library.Hook) error { +func (c *client) RedeliverWebhook(ctx context.Context, u *api.User, h *api.Hook) error { // create GitHub OAuth client with user's token - client := c.newClientToken(ctx, *u.Token) + client := c.newClientToken(ctx, u.GetToken()) // capture the delivery ID of the hook using GitHub API - deliveryID, err := c.getDeliveryID(ctx, client, r, h) + deliveryID, err := c.getDeliveryID(ctx, client, h) if err != nil { return err } // redeliver the webhook - _, _, err = client.Repositories.RedeliverHookDelivery(ctx, r.GetOrg(), r.GetName(), h.GetWebhookID(), deliveryID) + _, _, err = client.Repositories.RedeliverHookDelivery( + ctx, + h.GetRepo().GetOrg(), + h.GetRepo().GetName(), + h.GetWebhookID(), deliveryID, + ) if err != nil { var acceptedError *github.AcceptedError @@ -127,7 +132,7 @@ func (c *client) RedeliverWebhook(ctx context.Context, u *api.User, r *api.Repo, } // processPushEvent is a helper function to process the push event. -func (c *client) processPushEvent(ctx context.Context, h *library.Hook, payload *github.PushEvent) (*internal.Webhook, error) { +func (c *client) processPushEvent(ctx context.Context, h *api.Hook, payload *github.PushEvent) (*internal.Webhook, error) { c.Logger.WithFields(logrus.Fields{ "org": payload.GetRepo().GetOwner().GetLogin(), "repo": payload.GetRepo().GetName(), @@ -233,7 +238,7 @@ func (c *client) processPushEvent(ctx context.Context, h *library.Hook, payload } // processPREvent is a helper function to process the pull_request event. -func (c *client) processPREvent(h *library.Hook, payload *github.PullRequestEvent) (*internal.Webhook, error) { +func (c *client) processPREvent(h *api.Hook, payload *github.PullRequestEvent) (*internal.Webhook, error) { c.Logger.WithFields(logrus.Fields{ "org": payload.GetRepo().GetOwner().GetLogin(), "repo": payload.GetRepo().GetName(), @@ -342,7 +347,7 @@ func (c *client) processPREvent(h *library.Hook, payload *github.PullRequestEven } // processDeploymentEvent is a helper function to process the deployment event. -func (c *client) processDeploymentEvent(h *library.Hook, payload *github.DeploymentEvent) (*internal.Webhook, error) { +func (c *client) processDeploymentEvent(h *api.Hook, payload *github.DeploymentEvent) (*internal.Webhook, error) { c.Logger.WithFields(logrus.Fields{ "org": payload.GetRepo().GetOwner().GetLogin(), "repo": payload.GetRepo().GetName(), @@ -446,7 +451,7 @@ func (c *client) processDeploymentEvent(h *library.Hook, payload *github.Deploym } // processIssueCommentEvent is a helper function to process the issue comment event. -func (c *client) processIssueCommentEvent(h *library.Hook, payload *github.IssueCommentEvent) (*internal.Webhook, error) { +func (c *client) processIssueCommentEvent(h *api.Hook, payload *github.IssueCommentEvent) (*internal.Webhook, error) { c.Logger.WithFields(logrus.Fields{ "org": payload.GetRepo().GetOwner().GetLogin(), "repo": payload.GetRepo().GetName(), @@ -507,7 +512,7 @@ func (c *client) processIssueCommentEvent(h *library.Hook, payload *github.Issue // processRepositoryEvent is a helper function to process the repository event. -func (c *client) processRepositoryEvent(h *library.Hook, payload *github.RepositoryEvent) (*internal.Webhook, error) { +func (c *client) processRepositoryEvent(h *api.Hook, payload *github.RepositoryEvent) (*internal.Webhook, error) { logrus.Tracef("processing repository event GitHub webhook for %s", payload.GetRepo().GetFullName()) repo := payload.GetRepo() @@ -539,17 +544,23 @@ func (c *client) processRepositoryEvent(h *library.Hook, payload *github.Reposit // getDeliveryID gets the last 100 webhook deliveries for a repo and // finds the matching delivery id with the source id in the hook. -func (c *client) getDeliveryID(ctx context.Context, ghClient *github.Client, r *api.Repo, h *library.Hook) (int64, error) { +func (c *client) getDeliveryID(ctx context.Context, ghClient *github.Client, h *api.Hook) (int64, error) { c.Logger.WithFields(logrus.Fields{ - "org": r.GetOrg(), - "repo": r.GetName(), + "org": h.GetRepo().GetOrg(), + "repo": h.GetRepo().GetName(), }).Tracef("searching for delivery id for hook: %s", h.GetSourceID()) // set per page to 100 to retrieve last 100 hook summaries opt := &github.ListCursorOptions{PerPage: 100} // send API call to capture delivery summaries that contain Delivery ID value - deliveries, resp, err := ghClient.Repositories.ListHookDeliveries(ctx, r.GetOrg(), r.GetName(), h.GetWebhookID(), opt) + deliveries, resp, err := ghClient.Repositories.ListHookDeliveries( + ctx, + h.GetRepo().GetOrg(), + h.GetRepo().GetName(), + h.GetWebhookID(), + opt, + ) // version check: if GitHub API is older than version 3.2, this call will not work if resp.StatusCode == 415 { diff --git a/scm/github/webhook_test.go b/scm/github/webhook_test.go index 745b176a2..23c87be17 100644 --- a/scm/github/webhook_test.go +++ b/scm/github/webhook_test.go @@ -46,7 +46,7 @@ func TestGithub_ProcessWebhook_Push(t *testing.T) { client, _ := NewTest(s.URL) // run test - wantHook := new(library.Hook) + wantHook := new(api.Hook) wantHook.SetNumber(1) wantHook.SetSourceID("7bd477e4-4415-11e9-9359-0d41fdf9567e") wantHook.SetWebhookID(123456) @@ -125,7 +125,7 @@ func TestGithub_ProcessWebhook_Push_NoSender(t *testing.T) { client, _ := NewTest(s.URL) // run test - wantHook := new(library.Hook) + wantHook := new(api.Hook) wantHook.SetNumber(1) wantHook.SetSourceID("7bd477e4-4415-11e9-9359-0d41fdf9567e") wantHook.SetWebhookID(123456) @@ -202,7 +202,7 @@ func TestGithub_ProcessWebhook_Push_Branch_Delete(t *testing.T) { client, _ := NewTest(s.URL) // run test - wantHook := new(library.Hook) + wantHook := new(api.Hook) wantHook.SetNumber(1) wantHook.SetSourceID("7bd477e4-4415-11e9-9359-0d41fdf9567e") wantHook.SetWebhookID(123456) @@ -280,7 +280,7 @@ func TestGithub_ProcessWebhook_Push_Tag_Delete(t *testing.T) { client, _ := NewTest(s.URL) // run test - wantHook := new(library.Hook) + wantHook := new(api.Hook) wantHook.SetNumber(1) wantHook.SetSourceID("7bd477e4-4415-11e9-9359-0d41fdf9567e") wantHook.SetWebhookID(123456) @@ -340,7 +340,7 @@ func TestGithub_ProcessWebhook_PullRequest(t *testing.T) { defer s.Close() // run test - wantHook := new(library.Hook) + wantHook := new(api.Hook) wantHook.SetNumber(1) wantHook.SetSourceID("7bd477e4-4415-11e9-9359-0d41fdf9567e") wantHook.SetWebhookID(123456) @@ -574,7 +574,7 @@ func TestGithub_ProcessWebhook_Deployment(t *testing.T) { s := httptest.NewServer(http.NotFoundHandler()) defer s.Close() - wantHook := new(library.Hook) + wantHook := new(api.Hook) wantHook.SetNumber(1) wantHook.SetSourceID("7bd477e4-4415-11e9-9359-0d41fdf9567e") wantHook.SetWebhookID(123456) @@ -626,7 +626,7 @@ func TestGithub_ProcessWebhook_Deployment(t *testing.T) { type args struct { file string - hook *library.Hook + hook *api.Hook repo *api.Repo build *api.Build deploymentPayload raw.StringSliceMap @@ -710,7 +710,7 @@ func TestGithub_ProcessWebhook_Deployment_Commit(t *testing.T) { client, _ := NewTest(s.URL) // run test - wantHook := new(library.Hook) + wantHook := new(api.Hook) wantHook.SetNumber(1) wantHook.SetSourceID("7bd477e4-4415-11e9-9359-0d41fdf9567e") wantHook.SetWebhookID(123456) @@ -805,7 +805,7 @@ func TestGithub_ProcessWebhook_BadGithubEvent(t *testing.T) { client, _ := NewTest(s.URL) // run test - wantHook := new(library.Hook) + wantHook := new(api.Hook) wantHook.SetNumber(1) wantHook.SetSourceID("7bd477e4-4415-11e9-9359-0d41fdf9567e") wantHook.SetWebhookID(123456) @@ -858,7 +858,7 @@ func TestGithub_ProcessWebhook_BadContentType(t *testing.T) { client, _ := NewTest(s.URL) // run test - wantHook := new(library.Hook) + wantHook := new(api.Hook) wantHook.SetNumber(1) wantHook.SetSourceID("7bd477e4-4415-11e9-9359-0d41fdf9567e") wantHook.SetWebhookID(123456) @@ -984,7 +984,7 @@ func TestGithub_ProcessWebhook_IssueComment_PR(t *testing.T) { client, _ := NewTest(s.URL) // run test - wantHook := new(library.Hook) + wantHook := new(api.Hook) wantHook.SetNumber(1) wantHook.SetSourceID("7bd477e4-4415-11e9-9359-0d41fdf9567e") wantHook.SetWebhookID(123456) @@ -1064,7 +1064,7 @@ func TestGithub_ProcessWebhook_IssueComment_Created(t *testing.T) { client, _ := NewTest(s.URL) // run test - wantHook := new(library.Hook) + wantHook := new(api.Hook) wantHook.SetNumber(1) wantHook.SetSourceID("7bd477e4-4415-11e9-9359-0d41fdf9567e") wantHook.SetWebhookID(123456) @@ -1115,7 +1115,7 @@ func TestGithub_ProcessWebhook_IssueComment_Deleted(t *testing.T) { client, _ := NewTest(s.URL) // run test - wantHook := new(library.Hook) + wantHook := new(api.Hook) wantHook.SetNumber(1) wantHook.SetSourceID("7bd477e4-4415-11e9-9359-0d41fdf9567e") wantHook.SetWebhookID(123456) @@ -1164,7 +1164,7 @@ func TestGitHub_ProcessWebhook_RepositoryRename(t *testing.T) { client, _ := NewTest(s.URL) // run test - wantHook := new(library.Hook) + wantHook := new(api.Hook) wantHook.SetNumber(1) wantHook.SetSourceID("7bd477e4-4415-11e9-9359-0d41fdf9567e") wantHook.SetWebhookID(123456) @@ -1227,7 +1227,7 @@ func TestGitHub_ProcessWebhook_RepositoryTransfer(t *testing.T) { client, _ := NewTest(s.URL) // run test - wantHook := new(library.Hook) + wantHook := new(api.Hook) wantHook.SetNumber(1) wantHook.SetSourceID("7bd477e4-4415-11e9-9359-0d41fdf9567e") wantHook.SetWebhookID(123456) @@ -1290,7 +1290,7 @@ func TestGitHub_ProcessWebhook_RepositoryArchived(t *testing.T) { client, _ := NewTest(s.URL) // run test - wantHook := new(library.Hook) + wantHook := new(api.Hook) wantHook.SetNumber(1) wantHook.SetSourceID("7bd477e4-4415-11e9-9359-0d41fdf9567e") wantHook.SetWebhookID(123456) @@ -1353,7 +1353,7 @@ func TestGitHub_ProcessWebhook_RepositoryEdited(t *testing.T) { client, _ := NewTest(s.URL) // run test - wantHook := new(library.Hook) + wantHook := new(api.Hook) wantHook.SetNumber(1) wantHook.SetSourceID("7bd477e4-4415-11e9-9359-0d41fdf9567e") wantHook.SetWebhookID(123456) @@ -1416,7 +1416,7 @@ func TestGitHub_ProcessWebhook_Repository(t *testing.T) { client, _ := NewTest(s.URL) // run test - wantHook := new(library.Hook) + wantHook := new(api.Hook) wantHook.SetNumber(1) wantHook.SetSourceID("7bd477e4-4415-11e9-9359-0d41fdf9567e") wantHook.SetWebhookID(123456) @@ -1482,23 +1482,23 @@ func TestGithub_Redeliver_Webhook(t *testing.T) { u.SetName("octocat") u.SetToken("foo") - _hook := new(library.Hook) - _hook.SetSourceID("b595f0e0-aee1-11ec-86cf-9418381395c4") - _hook.SetID(1) - _hook.SetRepoID(1) - _hook.SetBuildID(1) - _hook.SetNumber(1) - _hook.SetWebhookID(1234) - _repo := new(api.Repo) _repo.SetID(1) + _repo.SetOwner(u) _repo.SetName("bar") _repo.SetOrg("foo") + _hook := new(api.Hook) + _hook.SetSourceID("b595f0e0-aee1-11ec-86cf-9418381395c4") + _hook.SetID(1) + _hook.SetRepo(_repo) + _hook.SetNumber(1) + _hook.SetWebhookID(1234) + client, _ := NewTest(s.URL, "https://foo.bar.com") // run test - err := client.RedeliverWebhook(context.TODO(), u, _repo, _hook) + err := client.RedeliverWebhook(context.TODO(), u, _hook) if err != nil { t.Errorf("RedeliverWebhook returned err: %v", err) @@ -1526,19 +1526,19 @@ func TestGithub_GetDeliveryID(t *testing.T) { u.SetName("octocat") u.SetToken("foo") - _hook := new(library.Hook) - _hook.SetSourceID("b595f0e0-aee1-11ec-86cf-9418381395c4") - _hook.SetID(1) - _hook.SetRepoID(1) - _hook.SetBuildID(1) - _hook.SetNumber(1) - _hook.SetWebhookID(1234) - _repo := new(api.Repo) _repo.SetID(1) + _repo.SetOwner(u) _repo.SetName("bar") _repo.SetOrg("foo") + _hook := new(api.Hook) + _hook.SetSourceID("b595f0e0-aee1-11ec-86cf-9418381395c4") + _hook.SetID(1) + _hook.SetRepo(_repo) + _hook.SetNumber(1) + _hook.SetWebhookID(1234) + want := int64(22948188373) client, _ := NewTest(s.URL, "https://foo.bar.com") @@ -1546,7 +1546,7 @@ func TestGithub_GetDeliveryID(t *testing.T) { ghClient := client.newClientToken(context.Background(), *u.Token) // run test - got, err := client.getDeliveryID(context.TODO(), ghClient, _repo, _hook) + got, err := client.getDeliveryID(context.TODO(), ghClient, _hook) if err != nil { t.Errorf("RedeliverWebhook returned err: %v", err) diff --git a/scm/service.go b/scm/service.go index 88bb5ecf3..697bcf8ef 100644 --- a/scm/service.go +++ b/scm/service.go @@ -110,7 +110,7 @@ type Service interface { Disable(context.Context, *api.User, string, string) error // Enable defines a function that activates // a repo by creating the webhook. - Enable(context.Context, *api.User, *api.Repo, *library.Hook) (*library.Hook, string, error) + Enable(context.Context, *api.User, *api.Repo, *api.Hook) (*api.Hook, string, error) // Update defines a function that updates // a webhook for a specified repo. Update(context.Context, *api.User, *api.Repo, int64) (bool, error) @@ -152,7 +152,7 @@ type Service interface { VerifyWebhook(context.Context, *http.Request, *api.Repo) error // RedeliverWebhook defines a function that // redelivers the webhook from the SCM. - RedeliverWebhook(context.Context, *api.User, *api.Repo, *library.Hook) error + RedeliverWebhook(context.Context, *api.User, *api.Hook) error // TODO: Add convert functions to interface? }