From eaed84c0351dc30be584c78f3b4bae0a68324de6 Mon Sep 17 00:00:00 2001 From: ecrupper Date: Mon, 30 Sep 2024 11:48:45 -0500 Subject: [PATCH 1/3] refactor(deployment): use server API types for deployment --- api/deployment/create.go | 6 +- api/types/build.go | 41 --- api/types/deployment.go | 398 ++++++++++++++++++++++ api/types/deployment_test.go | 222 ++++++++++++ api/webhook/post.go | 6 +- database/build/count_deployment.go | 4 +- database/build/count_deployment_test.go | 2 +- database/build/interface.go | 3 +- database/deployment/count_repo_test.go | 29 +- database/deployment/count_test.go | 53 +-- database/deployment/create.go | 10 +- database/deployment/create_test.go | 12 +- database/deployment/delete.go | 8 +- database/deployment/delete_test.go | 14 +- database/deployment/deployment.go | 2 + database/deployment/deployment_test.go | 50 +++ database/deployment/get.go | 23 +- database/deployment/get_repo.go | 22 +- database/deployment/get_repo_test.go | 84 +++-- database/deployment/get_test.go | 63 +++- database/deployment/interface.go | 15 +- database/deployment/list.go | 25 +- database/deployment/list_repo.go | 26 +- database/deployment/list_repo_test.go | 109 ++++-- database/deployment/list_test.go | 91 +++-- database/deployment/opts.go | 10 + database/deployment/update.go | 10 +- database/deployment/update_test.go | 12 +- database/integration_test.go | 91 +++-- database/resource.go | 1 + database/testutils/api_resources.go | 8 +- database/types/build.go | 11 +- database/types/deployment.go | 207 ++++++++++++ database/types/deployment_test.go | 241 ++++++++++++++ database/types/hook.go | 11 +- internal/webhook.go | 3 +- mock/server/deployment.go | 426 ++++++++++++++++++++++-- mock/server/deployment_test.go | 4 +- scm/github/deployment.go | 19 +- scm/github/deployment_test.go | 5 +- scm/github/webhook.go | 3 +- scm/github/webhook_test.go | 7 +- scm/service.go | 6 +- 43 files changed, 2030 insertions(+), 363 deletions(-) create mode 100644 api/types/deployment.go create mode 100644 api/types/deployment_test.go create mode 100644 database/types/deployment.go create mode 100644 database/types/deployment_test.go diff --git a/api/deployment/create.go b/api/deployment/create.go index fa980e5e1..f4c3f5ca1 100644 --- a/api/deployment/create.go +++ b/api/deployment/create.go @@ -10,12 +10,12 @@ 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/router/middleware/user" "github.com/go-vela/server/scm" "github.com/go-vela/server/util" - "github.com/go-vela/types/library" ) // swagger:operation POST /api/v1/deployments/{org}/{repo} deployments CreateDeployment @@ -72,7 +72,7 @@ func CreateDeployment(c *gin.Context) { l.Debugf("creating new deployment for repo %s", r.GetFullName()) // capture body from API request - input := new(library.Deployment) + input := new(types.Deployment) err := c.Bind(input) if err != nil { @@ -84,7 +84,7 @@ func CreateDeployment(c *gin.Context) { } // update fields in deployment object - input.SetRepoID(r.GetID()) + input.SetRepo(r) input.SetCreatedBy(u.GetName()) input.SetCreatedAt(time.Now().Unix()) diff --git a/api/types/build.go b/api/types/build.go index e44c7e21f..10ccc31e2 100644 --- a/api/types/build.go +++ b/api/types/build.go @@ -8,7 +8,6 @@ import ( "time" "github.com/go-vela/types/constants" - "github.com/go-vela/types/library" "github.com/go-vela/types/raw" ) @@ -1206,43 +1205,3 @@ func (b *Build) String() string { b.GetTitle(), ) } - -// TODO: remove this when Deployment is moved from types and uses Build type instead of library type. -func (b *Build) ToLibrary() *library.Build { - return &library.Build{ - ID: b.ID, - RepoID: b.GetRepo().ID, - PipelineID: b.PipelineID, - Number: b.Number, - Parent: b.Parent, - Event: b.Event, - EventAction: b.EventAction, - Status: b.Status, - Error: b.Error, - Enqueued: b.Enqueued, - Created: b.Created, - Started: b.Started, - Finished: b.Finished, - Deploy: b.Deploy, - DeployNumber: b.DeployNumber, - DeployPayload: b.DeployPayload, - Clone: b.Clone, - Source: b.Source, - Title: b.Title, - Message: b.Message, - Commit: b.Commit, - Sender: b.Sender, - Author: b.Author, - Email: b.Email, - Link: b.Link, - Branch: b.Branch, - Ref: b.Ref, - BaseRef: b.BaseRef, - HeadRef: b.HeadRef, - Host: b.Host, - Runtime: b.Runtime, - Distribution: b.Distribution, - ApprovedAt: b.ApprovedAt, - ApprovedBy: b.ApprovedBy, - } -} diff --git a/api/types/deployment.go b/api/types/deployment.go new file mode 100644 index 000000000..915fdd39c --- /dev/null +++ b/api/types/deployment.go @@ -0,0 +1,398 @@ +// SPDX-License-Identifier: Apache-2.0 + +package types + +import ( + "fmt" + + "github.com/go-vela/types/raw" +) + +// Deployment is the API representation of a deployment. +// +// swagger:model Deployment +type Deployment struct { + ID *int64 `json:"id,omitempty"` + Number *int64 `json:"number,omitempty"` + Repo *Repo `json:"repo,omitempty"` + URL *string `json:"url,omitempty"` + Commit *string `json:"commit,omitempty"` + Ref *string `json:"ref,omitempty"` + Task *string `json:"task,omitempty"` + Target *string `json:"target,omitempty"` + Description *string `json:"description,omitempty"` + Payload *raw.StringSliceMap `json:"payload,omitempty"` + CreatedAt *int64 `json:"created_at,omitempty"` + CreatedBy *string `json:"created_by,omitempty"` + Builds []*Build `json:"builds,omitempty"` +} + +// GetID returns the ID field. +// +// When the provided Deployment type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (d *Deployment) GetID() int64 { + // return zero value if Deployment type or ID field is nil + if d == nil || d.ID == nil { + return 0 + } + + return *d.ID +} + +// GetNumber returns the Number field. +// +// When the provided Deployment type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (d *Deployment) GetNumber() int64 { + // return zero value if Deployment type or ID field is nil + if d == nil || d.Number == nil { + return 0 + } + + return *d.Number +} + +// GetRepo returns the Repo field. +// +// When the provided Deployment type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (d *Deployment) GetRepo() *Repo { + // return zero value if Deployment type or Repo field is nil + if d == nil || d.Repo == nil { + return new(Repo) + } + + return d.Repo +} + +// GetURL returns the URL field. +// +// When the provided Deployment type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (d *Deployment) GetURL() string { + // return zero value if Deployment type or URL field is nil + if d == nil || d.URL == nil { + return "" + } + + return *d.URL +} + +// GetCommit returns the Commit field. +// +// When the provided Deployment type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (d *Deployment) GetCommit() string { + // return zero value if Deployment type or Commit field is nil + if d == nil || d.Commit == nil { + return "" + } + + return *d.Commit +} + +// GetRef returns the Ref field. +// +// When the provided Deployment type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (d *Deployment) GetRef() string { + // return zero value if Deployment type or Ref field is nil + if d == nil || d.Ref == nil { + return "" + } + + return *d.Ref +} + +// GetTask returns the Task field. +// +// When the provided Deployment type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (d *Deployment) GetTask() string { + // return zero value if Deployment type or Task field is nil + if d == nil || d.Task == nil { + return "" + } + + return *d.Task +} + +// GetTarget returns the Target field. +// +// When the provided Deployment type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (d *Deployment) GetTarget() string { + // return zero value if Deployment type or Target field is nil + if d == nil || d.Target == nil { + return "" + } + + return *d.Target +} + +// GetDescription returns the Description field. +// +// When the provided Deployment type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (d *Deployment) GetDescription() string { + // return zero value if Deployment type or Description field is nil + if d == nil || d.Description == nil { + return "" + } + + return *d.Description +} + +// GetPayload returns the Payload field. +// +// When the provided Deployment type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (d *Deployment) GetPayload() map[string]string { + // return zero value if Deployment type or Description field is nil + if d == nil || d.Payload == nil { + return map[string]string{} + } + + return *d.Payload +} + +// GetCreatedAt returns the CreatedAt field. +// +// When the provided Deployment type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (d *Deployment) GetCreatedAt() int64 { + // return zero value if Deployment type or CreatedAt field is nil + if d == nil || d.CreatedAt == nil { + return 0 + } + + return *d.CreatedAt +} + +// GetCreatedBy returns the CreatedBy field. +// +// When the provided Deployment type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (d *Deployment) GetCreatedBy() string { + // return zero value if Deployment type or CreatedBy field is nil + if d == nil || d.CreatedBy == nil { + return "" + } + + return *d.CreatedBy +} + +// GetBuilds returns the Builds field. +// +// When the provided Deployment type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (d *Deployment) GetBuilds() []*Build { + if d == nil || d.Builds == nil { + return []*Build{} + } + + return d.Builds +} + +// SetID sets the ID field. +// +// When the provided Deployment type is nil, it +// will set nothing and immediately return. +func (d *Deployment) SetID(v int64) { + // return if Deployment type is nil + if d == nil { + return + } + + d.ID = &v +} + +// SetNumber sets the Number field. +// +// When the provided Deployment type is nil, it +// will set nothing and immediately return. +func (d *Deployment) SetNumber(v int64) { + // return if Deployment type is nil + if d == nil { + return + } + + d.Number = &v +} + +// SetRepo sets the Repo field. +// +// When the provided Deployment type is nil, it +// will set nothing and immediately return. +func (d *Deployment) SetRepo(v *Repo) { + // return if Deployment type is nil + if d == nil { + return + } + + d.Repo = v +} + +// SetURL sets the URL field. +// +// When the provided Deployment type is nil, it +// will set nothing and immediately return. +func (d *Deployment) SetURL(v string) { + // return if Deployment type is nil + if d == nil { + return + } + + d.URL = &v +} + +// SetCommit sets the Commit field. +// +// When the provided Deployment type is nil, it +// will set nothing and immediately return. +func (d *Deployment) SetCommit(v string) { + // return if Deployment type is nil + if d == nil { + return + } + + d.Commit = &v +} + +// SetRef sets the Ref field. +// +// When the provided Deployment type is nil, it +// will set nothing and immediately return. +func (d *Deployment) SetRef(v string) { + // return if Deployment type is nil + if d == nil { + return + } + + d.Ref = &v +} + +// SetTask sets the Task field. +// +// When the provided Deployment type is nil, it +// will set nothing and immediately return. +func (d *Deployment) SetTask(v string) { + // return if Deployment type is nil + if d == nil { + return + } + + d.Task = &v +} + +// SetTarget sets the Target field. +// +// When the provided Deployment type is nil, it +// will set nothing and immediately return. +func (d *Deployment) SetTarget(v string) { + // return if Deployment type is nil + if d == nil { + return + } + + d.Target = &v +} + +// SetDescription sets the Description field. +// +// When the provided Deployment type is nil, it +// will set nothing and immediately return. +func (d *Deployment) SetDescription(v string) { + // return if Deployment type is nil + if d == nil { + return + } + + d.Description = &v +} + +// SetPayload sets the Payload field. +// +// When the provided Deployment type is nil, it +// will set nothing and immediately return. +func (d *Deployment) SetPayload(v raw.StringSliceMap) { + // return if Deployment type is nil + if d == nil { + return + } + + d.Payload = &v +} + +// SetCreatedAt sets the CreatedAt field. +// +// When the provided Deployment type is nil, it +// will set nothing and immediately return. +func (d *Deployment) SetCreatedAt(v int64) { + // return if Deployment type is nil + if d == nil { + return + } + + d.CreatedAt = &v +} + +// SetCreatedBy sets the CreatedBy field. +// +// When the provided Deployment type is nil, it +// will set nothing and immediately return. +func (d *Deployment) SetCreatedBy(v string) { + // return if Deployment type is nil + if d == nil { + return + } + + d.CreatedBy = &v +} + +// SetBuilds sets the Builds field. +// +// When the provided Deployment type is nil, it +// will set nothing and immediately return. +func (d *Deployment) SetBuilds(b []*Build) { + // return if Deployment type is nil + if d == nil { + return + } + + d.Builds = b +} + +// String implements the Stringer interface for the Deployment type. +func (d *Deployment) String() string { + return fmt.Sprintf(`{ + Commit: %s, + CreatedAt: %d, + CreatedBy: %s, + Description: %s, + ID: %d, + Number: %d, + Ref: %s, + Repo: %v, + Target: %s, + Task: %s, + URL: %s, + Payload: %s, + Builds: %d, +}`, + d.GetCommit(), + d.GetCreatedAt(), + d.GetCreatedBy(), + d.GetDescription(), + d.GetID(), + d.GetNumber(), + d.GetRef(), + d.GetRepo(), + d.GetTarget(), + d.GetTask(), + d.GetURL(), + d.GetPayload(), + len(d.GetBuilds()), + ) +} diff --git a/api/types/deployment_test.go b/api/types/deployment_test.go new file mode 100644 index 000000000..52cf9bc77 --- /dev/null +++ b/api/types/deployment_test.go @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: Apache-2.0 + +package types + +import ( + "fmt" + "reflect" + "testing" +) + +func TestTypes_Deployment_Getters(t *testing.T) { + // setup tests + tests := []struct { + deployment *Deployment + want *Deployment + }{ + { + deployment: testDeployment(), + want: testDeployment(), + }, + { + deployment: new(Deployment), + want: new(Deployment), + }, + } + + // run tests + for _, test := range tests { + if test.deployment.GetID() != test.want.GetID() { + t.Errorf("GetID is %v, want %v", test.deployment.GetID(), test.want.GetID()) + } + + if !reflect.DeepEqual(test.deployment.GetRepo(), test.want.GetRepo()) { + t.Errorf("GetRepoID is %v, want %v", test.deployment.GetRepo(), test.want.GetRepo()) + } + + if test.deployment.GetURL() != test.want.GetURL() { + t.Errorf("GetURL is %v, want %v", test.deployment.GetURL(), test.want.GetURL()) + } + + if test.deployment.GetCommit() != test.want.GetCommit() { + t.Errorf("GetCommit is %v, want %v", test.deployment.GetCommit(), test.want.GetCommit()) + } + + if test.deployment.GetRef() != test.want.GetRef() { + t.Errorf("GetRef is %v, want %v", test.deployment.GetRef(), test.want.GetRef()) + } + + if test.deployment.GetTask() != test.want.GetTask() { + t.Errorf("GetTask is %v, want %v", test.deployment.GetTask(), test.want.GetTask()) + } + + if test.deployment.GetTarget() != test.want.GetTarget() { + t.Errorf("GetTarget is %v, want %v", test.deployment.GetTarget(), test.want.GetTarget()) + } + + if test.deployment.GetDescription() != test.want.GetDescription() { + t.Errorf("GetDescription is %v, want %v", test.deployment.GetDescription(), test.want.GetDescription()) + } + + if !reflect.DeepEqual(test.deployment.GetPayload(), test.want.GetPayload()) { + t.Errorf("GetPayload is %v, want %v", test.deployment.GetPayload(), test.want.GetPayload()) + } + + if test.deployment.GetCreatedAt() != test.want.GetCreatedAt() { + t.Errorf("GetCreatedAt is %v, want %v", test.deployment.GetCreatedAt(), test.want.GetCreatedAt()) + } + + if test.deployment.GetCreatedBy() != test.want.GetCreatedBy() { + t.Errorf("GetCreatedBy is %v, want %v", test.deployment.GetCreatedBy(), test.want.GetCreatedBy()) + } + } +} + +func TestTypes_Deployment_Setters(t *testing.T) { + // setup types + var d *Deployment + + // setup tests + tests := []struct { + deployment *Deployment + want *Deployment + }{ + { + deployment: testDeployment(), + want: testDeployment(), + }, + { + deployment: d, + want: new(Deployment), + }, + } + + // run tests + for _, test := range tests { + test.deployment.SetID(test.want.GetID()) + test.deployment.SetNumber(test.want.GetNumber()) + test.deployment.SetRepo(test.want.GetRepo()) + test.deployment.SetURL(test.want.GetURL()) + test.deployment.SetCommit(test.want.GetCommit()) + test.deployment.SetRef(test.want.GetRef()) + test.deployment.SetTask(test.want.GetTask()) + test.deployment.SetTarget(test.want.GetTarget()) + test.deployment.SetDescription(test.want.GetDescription()) + test.deployment.SetPayload(test.want.GetPayload()) + test.deployment.SetCreatedAt(test.want.GetCreatedAt()) + test.deployment.SetCreatedBy(test.want.GetCreatedBy()) + + if test.deployment.GetID() != test.want.GetID() { + t.Errorf("SetID is %v, want %v", test.deployment.GetID(), test.want.GetID()) + } + + if test.deployment.GetNumber() != test.want.GetNumber() { + t.Errorf("SetNumber is %v, want %v", test.deployment.GetNumber(), test.want.GetNumber()) + } + + if !reflect.DeepEqual(test.deployment.GetRepo(), test.want.GetRepo()) { + t.Errorf("SetRepoID is %v, want %v", test.deployment.GetRepo(), test.want.GetRepo()) + } + + if test.deployment.GetURL() != test.want.GetURL() { + t.Errorf("SetURL is %v, want %v", test.deployment.GetURL(), test.want.GetURL()) + } + + if test.deployment.GetCommit() != test.want.GetCommit() { + t.Errorf("SetCommit is %v, want %v", test.deployment.GetCommit(), test.want.GetCommit()) + } + + if test.deployment.GetRef() != test.want.GetRef() { + t.Errorf("SetRef is %v, want %v", test.deployment.GetRef(), test.want.GetRef()) + } + + if test.deployment.GetTask() != test.want.GetTask() { + t.Errorf("SetTask is %v, want %v", test.deployment.GetTask(), test.want.GetTask()) + } + + if test.deployment.GetTarget() != test.want.GetTarget() { + t.Errorf("SetTarget is %v, want %v", test.deployment.GetTarget(), test.want.GetTarget()) + } + + if test.deployment.GetDescription() != test.want.GetDescription() { + t.Errorf("SetDescription is %v, want %v", test.deployment.GetDescription(), test.want.GetDescription()) + } + + if !reflect.DeepEqual(test.deployment.GetPayload(), test.want.GetPayload()) { + t.Errorf("SetPayload is %v, want %v", test.deployment.GetPayload(), test.want.GetPayload()) + } + + if test.deployment.GetCreatedAt() != test.want.GetCreatedAt() { + t.Errorf("SetCreatedAt is %v, want %v", test.deployment.GetCreatedAt(), test.want.GetCreatedAt()) + } + + if test.deployment.GetCreatedBy() != test.want.GetCreatedBy() { + t.Errorf("SetCreatedBy is %v, want %v", test.deployment.GetCreatedBy(), test.want.GetCreatedBy()) + } + } +} + +func TestTypes_Deployment_String(t *testing.T) { + // setup types + d := testDeployment() + + want := fmt.Sprintf(`{ + Commit: %s, + CreatedAt: %d, + CreatedBy: %s, + Description: %s, + ID: %d, + Number: %d, + Ref: %s, + Repo: %v, + Target: %s, + Task: %s, + URL: %s, + Payload: %s, + Builds: %d, +}`, + d.GetCommit(), + d.GetCreatedAt(), + d.GetCreatedBy(), + d.GetDescription(), + d.GetID(), + d.GetNumber(), + d.GetRef(), + d.GetRepo(), + d.GetTarget(), + d.GetTask(), + d.GetURL(), + d.GetPayload(), + len(d.GetBuilds()), + ) + + // run test + got := d.String() + + if !reflect.DeepEqual(got, want) { + t.Errorf("String is %v, want %v", got, want) + } +} + +// testDeployment is a test helper function to create a Deployment +// type with all fields set to a fake value. +func testDeployment() *Deployment { + d := new(Deployment) + + d.SetID(1) + d.SetNumber(0) + d.SetRepo(testRepo()) + d.SetURL("https://api.github.com/repos/github/octocat/deployments/1") + d.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135163") + d.SetRef("refs/heads/main") + d.SetTask("vela-deploy") + d.SetTarget("production") + d.SetDescription("Deployment request from Vela") + d.SetPayload(map[string]string{ + "foo": "test1", + }) + d.SetCreatedAt(1) + d.SetCreatedBy("octocat") + + return d +} diff --git a/api/webhook/post.go b/api/webhook/post.go index c83a600be..20576acd3 100644 --- a/api/webhook/post.go +++ b/api/webhook/post.go @@ -433,8 +433,8 @@ func PostWebhook(c *gin.Context) { if errors.Is(err, gorm.ErrRecordNotFound) { deployment := webhook.Deployment - deployment.SetRepoID(repo.GetID()) - deployment.SetBuilds([]*library.Build{b.ToLibrary()}) + deployment.SetRepo(repo) + deployment.SetBuilds([]*types.Build{b}) dr, err := database.FromContext(c).CreateDeployment(c, deployment) if err != nil { @@ -464,7 +464,7 @@ func PostWebhook(c *gin.Context) { return } } else { - build := append(d.GetBuilds(), b.ToLibrary()) + build := append(d.GetBuilds(), b) d.SetBuilds(build) diff --git a/database/build/count_deployment.go b/database/build/count_deployment.go index e00151cac..631228915 100644 --- a/database/build/count_deployment.go +++ b/database/build/count_deployment.go @@ -7,12 +7,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/library" ) // CountBuildsForDeployment gets the count of builds by deployment URL from the database. -func (e *engine) CountBuildsForDeployment(ctx context.Context, d *library.Deployment, filters map[string]interface{}) (int64, error) { +func (e *engine) CountBuildsForDeployment(ctx context.Context, d *api.Deployment, filters map[string]interface{}) (int64, error) { e.logger.WithFields(logrus.Fields{ "deployment": d.GetURL(), }).Tracef("getting count of builds for deployment %s", d.GetURL()) diff --git a/database/build/count_deployment_test.go b/database/build/count_deployment_test.go index 64fbbbbae..a956dbba5 100644 --- a/database/build/count_deployment_test.go +++ b/database/build/count_deployment_test.go @@ -49,7 +49,7 @@ func TestBuild_Engine_CountBuildsForDeployment(t *testing.T) { _deployment := testutils.APIDeployment() _deployment.SetID(1) - _deployment.SetRepoID(1) + _deployment.SetRepo(_repo) _deployment.SetURL("https://github.com/github/octocat/deployments/1") _postgres, _mock := testPostgres(t) diff --git a/database/build/interface.go b/database/build/interface.go index 01fad378a..fa330e00c 100644 --- a/database/build/interface.go +++ b/database/build/interface.go @@ -6,7 +6,6 @@ import ( "context" api "github.com/go-vela/server/api/types" - "github.com/go-vela/types/library" ) // BuildInterface represents the Vela interface for build @@ -32,7 +31,7 @@ type BuildInterface interface { // CountBuilds defines a function that gets the count of all builds. CountBuilds(context.Context) (int64, error) // CountBuildsForDeployment defines a function that gets the count of builds by deployment url. - CountBuildsForDeployment(context.Context, *library.Deployment, map[string]interface{}) (int64, error) + CountBuildsForDeployment(context.Context, *api.Deployment, map[string]interface{}) (int64, error) // CountBuildsForOrg defines a function that gets the count of builds by org name. CountBuildsForOrg(context.Context, string, map[string]interface{}) (int64, error) // CountBuildsForRepo defines a function that gets the count of builds by repo ID. diff --git a/database/deployment/count_repo_test.go b/database/deployment/count_repo_test.go index 775361570..7ef5e5995 100644 --- a/database/deployment/count_repo_test.go +++ b/database/deployment/count_repo_test.go @@ -9,17 +9,29 @@ import ( "github.com/DATA-DOG/go-sqlmock" + api "github.com/go-vela/server/api/types" "github.com/go-vela/server/database/testutils" - "github.com/go-vela/types/library" ) func TestDeployment_Engine_CountDeploymentsForRepo(t *testing.T) { - builds := []*library.Build{} + builds := []*api.Build{} // 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") + _deploymentOne := testutils.APIDeployment() _deploymentOne.SetID(1) - _deploymentOne.SetRepoID(1) + _deploymentOne.SetRepo(_repoOne) _deploymentOne.SetNumber(1) _deploymentOne.SetURL("https://github.com/github/octocat/deployments/1") _deploymentOne.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135163") @@ -34,7 +46,7 @@ func TestDeployment_Engine_CountDeploymentsForRepo(t *testing.T) { _deploymentTwo := testutils.APIDeployment() _deploymentTwo.SetID(2) - _deploymentTwo.SetRepoID(2) + _deploymentTwo.SetRepo(_repoTwo) _deploymentTwo.SetNumber(2) _deploymentTwo.SetURL("https://github.com/github/octocat/deployments/2") _deploymentTwo.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135164") @@ -47,13 +59,6 @@ func TestDeployment_Engine_CountDeploymentsForRepo(t *testing.T) { _deploymentTwo.SetCreatedBy("octocat") _deploymentTwo.SetBuilds(builds) - _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() }() @@ -100,7 +105,7 @@ func TestDeployment_Engine_CountDeploymentsForRepo(t *testing.T) { // run tests for _, test := range tests { t.Run(test.name, func(t *testing.T) { - got, err := test.database.CountDeploymentsForRepo(context.TODO(), _repo) + got, err := test.database.CountDeploymentsForRepo(context.TODO(), _repoOne) if test.failure { if err == nil { diff --git a/database/deployment/count_test.go b/database/deployment/count_test.go index 914bb22a5..582c79273 100644 --- a/database/deployment/count_test.go +++ b/database/deployment/count_test.go @@ -9,52 +9,21 @@ import ( "github.com/DATA-DOG/go-sqlmock" + api "github.com/go-vela/server/api/types" "github.com/go-vela/server/database/testutils" - "github.com/go-vela/types/library" - "github.com/go-vela/types/raw" ) func TestDeployment_Engine_CountDeployments(t *testing.T) { - buildOne := new(library.Build) - buildOne.SetID(1) - buildOne.SetRepoID(1) - buildOne.SetPipelineID(1) - buildOne.SetNumber(1) - buildOne.SetParent(1) - buildOne.SetEvent("push") - buildOne.SetEventAction("") - buildOne.SetStatus("running") - buildOne.SetError("") - buildOne.SetEnqueued(1563474077) - buildOne.SetCreated(1563474076) - buildOne.SetStarted(1563474078) - buildOne.SetFinished(1563474079) - buildOne.SetDeploy("") - buildOne.SetDeployPayload(raw.StringSliceMap{"foo": "test1"}) - buildOne.SetClone("https://github.com/github/octocat.git") - buildOne.SetSource("https://github.com/github/octocat/deployments/1") - buildOne.SetTitle("push received from https://github.com/github/octocat") - buildOne.SetMessage("First commit...") - buildOne.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135163") - buildOne.SetSender("OctoKitty") - buildOne.SetAuthor("OctoKitty") - buildOne.SetEmail("OctoKitty@github.com") - buildOne.SetLink("https://example.company.com/github/octocat/1") - buildOne.SetBranch("main") - buildOne.SetRef("refs/heads/main") - buildOne.SetBaseRef("") - buildOne.SetHeadRef("changes") - buildOne.SetHost("example.company.com") - buildOne.SetRuntime("docker") - buildOne.SetDistribution("linux") - - builds := []*library.Build{} - builds = append(builds, buildOne) - // setup types + _repoOne := testutils.APIRepo() + _repoOne.SetID(1) + _repoOne.SetOrg("foo") + _repoOne.SetName("bar") + _repoOne.SetFullName("foo/bar") + _deploymentOne := testutils.APIDeployment() _deploymentOne.SetID(1) - _deploymentOne.SetRepoID(1) + _deploymentOne.SetRepo(_repoOne) _deploymentOne.SetNumber(1) _deploymentOne.SetURL("https://github.com/github/octocat/deployments/1") _deploymentOne.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135163") @@ -65,11 +34,11 @@ func TestDeployment_Engine_CountDeployments(t *testing.T) { _deploymentOne.SetPayload(map[string]string{"foo": "test1"}) _deploymentOne.SetCreatedAt(1) _deploymentOne.SetCreatedBy("octocat") - _deploymentOne.SetBuilds(builds) + _deploymentOne.SetBuilds([]*api.Build{testutils.APIBuild()}) _deploymentTwo := testutils.APIDeployment() _deploymentTwo.SetID(2) - _deploymentTwo.SetRepoID(2) + _deploymentTwo.SetRepo(_repoOne) _deploymentTwo.SetNumber(2) _deploymentTwo.SetURL("https://github.com/github/octocat/deployments/2") _deploymentTwo.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135164") @@ -80,7 +49,7 @@ func TestDeployment_Engine_CountDeployments(t *testing.T) { _deploymentTwo.SetPayload(map[string]string{"foo": "test1"}) _deploymentTwo.SetCreatedAt(1) _deploymentTwo.SetCreatedBy("octocat") - _deploymentTwo.SetBuilds(builds) + _deploymentTwo.SetBuilds([]*api.Build{testutils.APIBuild()}) _postgres, _mock := testPostgres(t) defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() diff --git a/database/deployment/create.go b/database/deployment/create.go index 923625bb6..b3c85a188 100644 --- a/database/deployment/create.go +++ b/database/deployment/create.go @@ -7,19 +7,19 @@ import ( "github.com/sirupsen/logrus" + 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" ) // CreateDeployment creates a new deployment in the database. -func (e *engine) CreateDeployment(ctx context.Context, d *library.Deployment) (*library.Deployment, error) { +func (e *engine) CreateDeployment(ctx context.Context, d *api.Deployment) (*api.Deployment, error) { e.logger.WithFields(logrus.Fields{ "deployment": d.GetID(), }).Tracef("creating deployment %d", d.GetID()) // cast the library type to database type - deployment := database.DeploymentFromLibrary(d) + deployment := types.DeploymentFromAPI(d) // validate the necessary fields are populated err := deployment.Validate() @@ -33,5 +33,5 @@ func (e *engine) CreateDeployment(ctx context.Context, d *library.Deployment) (* Create(deployment) // send query to the database - return deployment.ToLibrary(d.Builds), result.Error + return deployment.ToAPI(d.Builds), result.Error } diff --git a/database/deployment/create_test.go b/database/deployment/create_test.go index 92a2e3448..e43179b94 100644 --- a/database/deployment/create_test.go +++ b/database/deployment/create_test.go @@ -9,16 +9,19 @@ import ( "github.com/DATA-DOG/go-sqlmock" "github.com/go-vela/server/database/testutils" - "github.com/go-vela/types/library" ) func TestDeployment_Engine_CreateDeployment(t *testing.T) { - builds := []*library.Build{} - // setup types + _repoOne := testutils.APIRepo() + _repoOne.SetID(1) + _repoOne.SetOrg("foo") + _repoOne.SetName("bar") + _repoOne.SetFullName("foo/bar") + _deploymentOne := testutils.APIDeployment() _deploymentOne.SetID(1) - _deploymentOne.SetRepoID(1) + _deploymentOne.SetRepo(_repoOne) _deploymentOne.SetNumber(1) _deploymentOne.SetURL("https://github.com/github/octocat/deployments/1") _deploymentOne.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135163") @@ -29,7 +32,6 @@ func TestDeployment_Engine_CreateDeployment(t *testing.T) { _deploymentOne.SetPayload(map[string]string{"foo": "test1"}) _deploymentOne.SetCreatedAt(1) _deploymentOne.SetCreatedBy("octocat") - _deploymentOne.SetBuilds(builds) _postgres, _mock := testPostgres(t) defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() diff --git a/database/deployment/delete.go b/database/deployment/delete.go index cdb98fe31..b0f76c7fc 100644 --- a/database/deployment/delete.go +++ b/database/deployment/delete.go @@ -7,19 +7,19 @@ import ( "github.com/sirupsen/logrus" + 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" ) // DeleteDeployment deletes an existing deployment from the database. -func (e *engine) DeleteDeployment(ctx context.Context, d *library.Deployment) error { +func (e *engine) DeleteDeployment(ctx context.Context, d *api.Deployment) error { e.logger.WithFields(logrus.Fields{ "deployment": d.GetID(), }).Tracef("deleting deployment %d", d.GetID()) // cast the library type to database type - deployment := database.DeploymentFromLibrary(d) + deployment := types.DeploymentFromAPI(d) // send query to the database return e.client. diff --git a/database/deployment/delete_test.go b/database/deployment/delete_test.go index e037c684c..3d1f44f16 100644 --- a/database/deployment/delete_test.go +++ b/database/deployment/delete_test.go @@ -8,17 +8,21 @@ import ( "github.com/DATA-DOG/go-sqlmock" + api "github.com/go-vela/server/api/types" "github.com/go-vela/server/database/testutils" - "github.com/go-vela/types/library" ) func TestDeployment_Engine_DeleteDeployment(t *testing.T) { - builds := []*library.Build{} - // setup types + _repoOne := testutils.APIRepo() + _repoOne.SetID(1) + _repoOne.SetOrg("foo") + _repoOne.SetName("bar") + _repoOne.SetFullName("foo/bar") + _deploymentOne := testutils.APIDeployment() _deploymentOne.SetID(1) - _deploymentOne.SetRepoID(1) + _deploymentOne.SetRepo(_repoOne) _deploymentOne.SetNumber(1) _deploymentOne.SetURL("https://github.com/github/octocat/deployments/1") _deploymentOne.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135163") @@ -29,7 +33,7 @@ func TestDeployment_Engine_DeleteDeployment(t *testing.T) { _deploymentOne.SetPayload(map[string]string{"foo": "test1"}) _deploymentOne.SetCreatedAt(1) _deploymentOne.SetCreatedBy("octocat") - _deploymentOne.SetBuilds(builds) + _deploymentOne.SetBuilds([]*api.Build{testutils.APIBuild()}) _postgres, _mock := testPostgres(t) defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() diff --git a/database/deployment/deployment.go b/database/deployment/deployment.go index cd9192438..e1fbefad0 100644 --- a/database/deployment/deployment.go +++ b/database/deployment/deployment.go @@ -15,6 +15,8 @@ import ( type ( // config represents the settings required to create the engine that implements the DeploymentInterface interface. config struct { + // specifies the encryption key to use for the Hook engine + EncryptionKey string // specifies to skip creating tables and indexes for the Deployment engine SkipCreation bool } diff --git a/database/deployment/deployment_test.go b/database/deployment/deployment_test.go index 6ab73474f..cba36347c 100644 --- a/database/deployment/deployment_test.go +++ b/database/deployment/deployment_test.go @@ -3,10 +3,14 @@ package deployment import ( + "context" "reflect" "testing" "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/types" "github.com/sirupsen/logrus" "gorm.io/driver/postgres" "gorm.io/driver/sqlite" @@ -161,3 +165,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, deployments []*api.Deployment, users []*api.User, repos []*api.Repo, builds []*api.Build) { + for _, _deployment := range deployments { + _, err := e.CreateDeployment(context.TODO(), _deployment) + 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/deployment/get.go b/database/deployment/get.go index 0086a03a2..aef68bc7f 100644 --- a/database/deployment/get.go +++ b/database/deployment/get.go @@ -6,22 +6,24 @@ import ( "context" "strconv" + 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" ) // GetDeployment gets a deployment by ID from the database. -func (e *engine) GetDeployment(ctx context.Context, id int64) (*library.Deployment, error) { +func (e *engine) GetDeployment(ctx context.Context, id int64) (*api.Deployment, error) { e.logger.Tracef("getting deployment %d", id) // variable to store query results - d := new(database.Deployment) + d := new(types.Deployment) // send query to the database and store result in variable err := e.client. WithContext(ctx). Table(constants.TableDeployment). + Preload("Repo"). + Preload("Repo.Owner"). Where("id = ?", id). Take(d). Error @@ -29,7 +31,7 @@ func (e *engine) GetDeployment(ctx context.Context, id int64) (*library.Deployme return nil, err } - builds := []*library.Build{} + builds := []*api.Build{} for _, a := range d.Builds { bID, err := strconv.ParseInt(a, 10, 64) @@ -37,7 +39,7 @@ func (e *engine) GetDeployment(ctx context.Context, id int64) (*library.Deployme return nil, err } // variable to store query results - b := new(database.Build) + b := new(types.Build) // send query to the database and store result in variable err = e.client. @@ -50,9 +52,14 @@ func (e *engine) GetDeployment(ctx context.Context, id int64) (*library.Deployme return nil, err } - builds = append(builds, b.ToLibrary()) + builds = append(builds, b.ToAPI()) + } + + err = d.Repo.Decrypt(e.config.EncryptionKey) + if err != nil { + e.logger.Errorf("unable to decrypt repo: %v", err) } // return the deployment - return d.ToLibrary(builds), nil + return d.ToAPI(builds), nil } diff --git a/database/deployment/get_repo.go b/database/deployment/get_repo.go index f50ef3aba..c96c8c6be 100644 --- a/database/deployment/get_repo.go +++ b/database/deployment/get_repo.go @@ -9,13 +9,12 @@ import ( "github.com/sirupsen/logrus" 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" ) // GetDeploymentForRepo gets a deployment by repo ID and number from the database. -func (e *engine) GetDeploymentForRepo(ctx context.Context, r *api.Repo, number int64) (*library.Deployment, error) { +func (e *engine) GetDeploymentForRepo(ctx context.Context, r *api.Repo, number int64) (*api.Deployment, error) { e.logger.WithFields(logrus.Fields{ "deployment": number, "org": r.GetOrg(), @@ -23,12 +22,14 @@ func (e *engine) GetDeploymentForRepo(ctx context.Context, r *api.Repo, number i }).Tracef("getting deployment %s/%d", r.GetFullName(), number) // variable to store query results - d := new(database.Deployment) + d := new(types.Deployment) // send query to the database and store result in variable err := e.client. WithContext(ctx). Table(constants.TableDeployment). + Preload("Repo"). + Preload("Repo.Owner"). Where("repo_id = ?", r.GetID()). Where("number = ?", number). Take(d). @@ -37,7 +38,7 @@ func (e *engine) GetDeploymentForRepo(ctx context.Context, r *api.Repo, number i return nil, err } - builds := []*library.Build{} + builds := []*api.Build{} for _, a := range d.Builds { bID, err := strconv.ParseInt(a, 10, 64) @@ -45,7 +46,7 @@ func (e *engine) GetDeploymentForRepo(ctx context.Context, r *api.Repo, number i return nil, err } // variable to store query results - b := new(database.Build) + b := new(types.Build) // send query to the database and store result in variable err = e.client. @@ -58,8 +59,13 @@ func (e *engine) GetDeploymentForRepo(ctx context.Context, r *api.Repo, number i return nil, err } - builds = append(builds, b.ToLibrary()) + builds = append(builds, b.ToAPI()) } - return d.ToLibrary(builds), nil + err = d.Repo.Decrypt(e.config.EncryptionKey) + if err != nil { + e.logger.Errorf("unable to decrypt repo %s/%s: %v", r.GetOrg(), r.GetName(), err) + } + + return d.ToAPI(builds), nil } diff --git a/database/deployment/get_repo_test.go b/database/deployment/get_repo_test.go index 78070f8c3..3bd86ab29 100644 --- a/database/deployment/get_repo_test.go +++ b/database/deployment/get_repo_test.go @@ -4,22 +4,44 @@ package deployment 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 TestDeployment_Engine_GetDeploymentForRepo(t *testing.T) { - builds := []*library.Build{} - // 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{}) + + _build := testutils.APIBuild() + _build.SetID(1) + _build.SetNumber(1) + _build.SetDeployNumber(0) + _build.SetDeployPayload(nil) + _deploymentOne := testutils.APIDeployment() _deploymentOne.SetID(1) - _deploymentOne.SetRepoID(1) + _deploymentOne.SetRepo(_repo) _deploymentOne.SetNumber(1) _deploymentOne.SetURL("https://github.com/github/octocat/deployments/1") _deploymentOne.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135163") @@ -30,14 +52,7 @@ func TestDeployment_Engine_GetDeploymentForRepo(t *testing.T) { _deploymentOne.SetPayload(map[string]string{"foo": "test1"}) _deploymentOne.SetCreatedAt(1) _deploymentOne.SetCreatedBy("octocat") - _deploymentOne.SetBuilds(builds) - - _repo := testutils.APIRepo() - _repo.SetID(1) - _repo.GetOwner().SetID(1) - _repo.SetOrg("foo") - _repo.SetName("bar") - _repo.SetFullName("foo/bar") + _deploymentOne.SetBuilds([]*api.Build{_build}) _postgres, _mock := testPostgres(t) defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() @@ -45,25 +60,44 @@ func TestDeployment_Engine_GetDeploymentForRepo(t *testing.T) { // create expected result in mock _rows := sqlmock.NewRows( []string{"id", "repo_id", "number", "url", "commit", "ref", "task", "target", "description", "payload", "created_at", "created_by", "builds"}). - AddRow(1, 1, 1, "https://github.com/github/octocat/deployments/1", "48afb5bdc41ad69bf22588491333f7cf71135163", "refs/heads/master", "vela-deploy", "production", "Deployment request from Vela", "{\"foo\":\"test1\"}", 1, "octocat", "{}") + AddRow(1, 1, 1, "https://github.com/github/octocat/deployments/1", "48afb5bdc41ad69bf22588491333f7cf71135163", "refs/heads/master", "vela-deploy", "production", "Deployment request from Vela", "{\"foo\":\"test1\"}", 1, "octocat", "{1}") + + _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) + + _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) // ensure the mock expects the query _mock.ExpectQuery(`SELECT * FROM "deployments" WHERE repo_id = $1 AND number = $2 LIMIT $3`).WithArgs(1, 1, 1).WillReturnRows(_rows) + _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) + _mock.ExpectQuery(`SELECT * FROM "builds" WHERE id = $1 LIMIT $2`).WithArgs(1, 1).WillReturnRows(_buildRows) _sqlite := testSqlite(t) defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() - _, err := _sqlite.CreateDeployment(context.TODO(), _deploymentOne) - if err != nil { - t.Errorf("unable to create test deployment for sqlite: %v", err) - } + sqlitePopulateTables( + t, + _sqlite, + []*api.Deployment{_deploymentOne}, + []*api.User{_owner}, + []*api.Repo{_repo}, + []*api.Build{_build}, + ) // setup tests tests := []struct { failure bool name string database *engine - want *library.Deployment + want *api.Deployment }{ { failure: false, @@ -96,8 +130,16 @@ func TestDeployment_Engine_GetDeploymentForRepo(t *testing.T) { t.Errorf("GetDeploymentForRepo for %s returned err: %v", test.name, err) } - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetDeploymentForRepo for %s is %v, want %v", test.name, got, test.want) + if diff := cmp.Diff( + test.want, + got, + cmp.Options{ + cmp.FilterPath(func(p cmp.Path) bool { + return p.String() == "Builds.Repo.Owner" // ignore this nested struct due to ToAPI + }, cmp.Ignore()), + }, + ); diff != "" { + t.Errorf("GetDeploymentForRepo for %s is a mismatch (-want +got):\n%s", test.name, diff) } }) } diff --git a/database/deployment/get_test.go b/database/deployment/get_test.go index b579aeecd..c1124b08d 100644 --- a/database/deployment/get_test.go +++ b/database/deployment/get_test.go @@ -4,22 +4,38 @@ package deployment 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 TestDeployment_Engine_GetDeployment(t *testing.T) { - builds := []*library.Build{} - // 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{}) + _deploymentOne := testutils.APIDeployment() _deploymentOne.SetID(1) - _deploymentOne.SetRepoID(1) + _deploymentOne.SetRepo(_repo) _deploymentOne.SetNumber(1) _deploymentOne.SetURL("https://github.com/github/octocat/deployments/1") _deploymentOne.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135163") @@ -30,7 +46,6 @@ func TestDeployment_Engine_GetDeployment(t *testing.T) { _deploymentOne.SetPayload(map[string]string{"foo": "test1"}) _deploymentOne.SetCreatedAt(1) _deploymentOne.SetCreatedBy("octocat") - _deploymentOne.SetBuilds(builds) _postgres, _mock := testPostgres(t) defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() @@ -40,23 +55,37 @@ func TestDeployment_Engine_GetDeployment(t *testing.T) { []string{"id", "repo_id", "number", "url", "commit", "ref", "task", "target", "description", "payload", "created_at", "created_by", "builds"}). AddRow(1, 1, 1, "https://github.com/github/octocat/deployments/1", "48afb5bdc41ad69bf22588491333f7cf71135163", "refs/heads/master", "vela-deploy", "production", "Deployment request from Vela", "{\"foo\":\"test1\"}", 1, "octocat", "{}") + _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 "deployments" WHERE id = $1 LIMIT $2`).WithArgs(1, 1).WillReturnRows(_rows) + _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.CreateDeployment(context.TODO(), _deploymentOne) - if err != nil { - t.Errorf("unable to create test deployment for sqlite: %v", err) - } + sqlitePopulateTables( + t, + _sqlite, + []*api.Deployment{_deploymentOne}, + []*api.User{_owner}, + []*api.Repo{_repo}, + []*api.Build{}, + ) // setup tests tests := []struct { failure bool name string database *engine - want *library.Deployment + want *api.Deployment }{ { failure: false, @@ -89,8 +118,16 @@ func TestDeployment_Engine_GetDeployment(t *testing.T) { t.Errorf("GetDeployment for %s returned err: %v", test.name, err) } - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetDeployment for %s is %v, want %v", test.name, got, test.want) + if diff := cmp.Diff( + test.want, + got, + cmp.Options{ + cmp.FilterPath(func(p cmp.Path) bool { + return p.String() == "Builds.Repo.Owner" // ignore this nested struct due to ToAPI + }, cmp.Ignore()), + }, + ); diff != "" { + t.Errorf("GetDeployment for %s is a mismatch (-want +got):\n%s", test.name, diff) } }) } diff --git a/database/deployment/interface.go b/database/deployment/interface.go index cd1f6cebc..3a6ba37b5 100644 --- a/database/deployment/interface.go +++ b/database/deployment/interface.go @@ -6,7 +6,6 @@ import ( "context" api "github.com/go-vela/server/api/types" - "github.com/go-vela/types/library" ) // DeploymentInterface represents the Vela interface for deployment @@ -32,17 +31,17 @@ type DeploymentInterface interface { // CountDeploymentsForRepo defines a function that gets the count of deployments by repo ID. CountDeploymentsForRepo(context.Context, *api.Repo) (int64, error) // CreateDeployment defines a function that creates a new deployment. - CreateDeployment(context.Context, *library.Deployment) (*library.Deployment, error) + CreateDeployment(context.Context, *api.Deployment) (*api.Deployment, error) // DeleteDeployment defines a function that deletes an existing deployment. - DeleteDeployment(context.Context, *library.Deployment) error + DeleteDeployment(context.Context, *api.Deployment) error // GetDeployment defines a function that gets a deployment by ID. - GetDeployment(context.Context, int64) (*library.Deployment, error) + GetDeployment(context.Context, int64) (*api.Deployment, error) // GetDeploymentForRepo defines a function that gets a deployment by repo ID and number. - GetDeploymentForRepo(context.Context, *api.Repo, int64) (*library.Deployment, error) + GetDeploymentForRepo(context.Context, *api.Repo, int64) (*api.Deployment, error) // ListDeployments defines a function that gets a list of all deployments. - ListDeployments(context.Context) ([]*library.Deployment, error) + ListDeployments(context.Context) ([]*api.Deployment, error) // ListDeploymentsForRepo defines a function that gets a list of deployments by repo ID. - ListDeploymentsForRepo(context.Context, *api.Repo, int, int) ([]*library.Deployment, error) + ListDeploymentsForRepo(context.Context, *api.Repo, int, int) ([]*api.Deployment, error) // UpdateDeployment defines a function that updates an existing deployment. - UpdateDeployment(context.Context, *library.Deployment) (*library.Deployment, error) + UpdateDeployment(context.Context, *api.Deployment) (*api.Deployment, error) } diff --git a/database/deployment/list.go b/database/deployment/list.go index 7f942f859..b5101eb06 100644 --- a/database/deployment/list.go +++ b/database/deployment/list.go @@ -6,23 +6,25 @@ import ( "context" "strconv" + 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" ) // ListDeployments gets a list of all deployments from the database. -func (e *engine) ListDeployments(ctx context.Context) ([]*library.Deployment, error) { +func (e *engine) ListDeployments(ctx context.Context) ([]*api.Deployment, error) { e.logger.Trace("listing all deployments") // variables to store query results and return value - d := new([]database.Deployment) - deployments := []*library.Deployment{} + d := new([]types.Deployment) + deployments := []*api.Deployment{} // send query to the database and store result in variable err := e.client. WithContext(ctx). Table(constants.TableDeployment). + Preload("Repo"). + Preload("Repo.Owner"). Find(&d). Error if err != nil { @@ -34,7 +36,7 @@ func (e *engine) ListDeployments(ctx context.Context) ([]*library.Deployment, er // https://golang.org/doc/faq#closures_and_goroutines tmp := deployment - builds := []*library.Build{} + builds := []*api.Build{} for _, a := range tmp.Builds { bID, err := strconv.ParseInt(a, 10, 64) @@ -42,7 +44,7 @@ func (e *engine) ListDeployments(ctx context.Context) ([]*library.Deployment, er return nil, err } // variable to store query results - b := new(database.Build) + b := new(types.Build) // send query to the database and store result in variable err = e.client. @@ -55,11 +57,16 @@ func (e *engine) ListDeployments(ctx context.Context) ([]*library.Deployment, er return nil, err } - builds = append(builds, b.ToLibrary()) + builds = append(builds, b.ToAPI()) + } + + err = tmp.Repo.Decrypt(e.config.EncryptionKey) + if err != nil { + e.logger.Errorf("unable to decrypt repo: %v", err) } // convert query result to library type - deployments = append(deployments, tmp.ToLibrary(builds)) + deployments = append(deployments, tmp.ToAPI(builds)) } return deployments, nil diff --git a/database/deployment/list_repo.go b/database/deployment/list_repo.go index 4be4e071b..b8b77fdad 100644 --- a/database/deployment/list_repo.go +++ b/database/deployment/list_repo.go @@ -9,21 +9,20 @@ import ( "github.com/sirupsen/logrus" 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" ) // ListDeploymentsForRepo gets a list of deployments by repo ID from the database. -func (e *engine) ListDeploymentsForRepo(ctx context.Context, r *api.Repo, page, perPage int) ([]*library.Deployment, error) { +func (e *engine) ListDeploymentsForRepo(ctx context.Context, r *api.Repo, page, perPage int) ([]*api.Deployment, error) { e.logger.WithFields(logrus.Fields{ "org": r.GetOrg(), "repo": r.GetName(), }).Tracef("listing deployments for repo %s", r.GetFullName()) // variables to store query results and return value - d := new([]database.Deployment) - deployments := []*library.Deployment{} + d := new([]types.Deployment) + deployments := []*api.Deployment{} // calculate offset for pagination through results offset := perPage * (page - 1) @@ -32,6 +31,8 @@ func (e *engine) ListDeploymentsForRepo(ctx context.Context, r *api.Repo, page, err := e.client. WithContext(ctx). Table(constants.TableDeployment). + Preload("Repo"). + Preload("Repo.Owner"). Where("repo_id = ?", r.GetID()). Order("number DESC"). Limit(perPage). @@ -47,7 +48,7 @@ func (e *engine) ListDeploymentsForRepo(ctx context.Context, r *api.Repo, page, // https://golang.org/doc/faq#closures_and_goroutines tmp := deployment - builds := []*library.Build{} + builds := []*api.Build{} for _, a := range tmp.Builds { bID, err := strconv.ParseInt(a, 10, 64) @@ -55,7 +56,7 @@ func (e *engine) ListDeploymentsForRepo(ctx context.Context, r *api.Repo, page, return nil, err } // variable to store query results - b := new(database.Build) + b := new(types.Build) // send query to the database and store result in variable err = e.client. @@ -68,11 +69,16 @@ func (e *engine) ListDeploymentsForRepo(ctx context.Context, r *api.Repo, page, return nil, err } - builds = append(builds, b.ToLibrary()) + builds = append(builds, b.ToAPI()) } - // convert query result to library type - deployments = append(deployments, tmp.ToLibrary(builds)) + err = tmp.Repo.Decrypt(e.config.EncryptionKey) + if err != nil { + e.logger.Errorf("unable to decrypt repo %s/%s: %v", r.GetOrg(), r.GetName(), err) + } + + // convert query result to API type + deployments = append(deployments, tmp.ToAPI(builds)) } return deployments, nil diff --git a/database/deployment/list_repo_test.go b/database/deployment/list_repo_test.go index c93de0939..777de2ba4 100644 --- a/database/deployment/list_repo_test.go +++ b/database/deployment/list_repo_test.go @@ -4,28 +4,56 @@ package deployment 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 TestDeployment_Engine_ListDeploymentsForRepo(t *testing.T) { - _repo := testutils.APIRepo() - _repo.SetID(1) - _repo.SetOrg("foo") - _repo.SetName("bar") - _repo.SetFullName("foo/bar") - - builds := []*library.Build{} - // setup types + _owner := testutils.APIUser().Crop() + _owner.SetID(1) + _owner.SetName("foo") + _owner.SetToken("bar") + + _repoOne := testutils.APIRepo() + _repoOne.SetID(1) + _repoOne.SetOwner(_owner) + _repoOne.SetHash("baz") + _repoOne.SetOrg("foo") + _repoOne.SetName("bar") + _repoOne.SetFullName("foo/bar") + _repoOne.SetVisibility("public") + _repoOne.SetAllowEvents(api.NewEventsFromMask(1)) + _repoOne.SetPipelineType(constants.PipelineTypeYAML) + _repoOne.SetTopics([]string{}) + + _repoTwo := testutils.APIRepo() + _repoTwo.SetID(2) + _repoTwo.SetOwner(_owner) + _repoTwo.SetHash("bazey") + _repoTwo.SetOrg("fooey") + _repoTwo.SetName("barey") + _repoTwo.SetFullName("fooey/barey") + _repoTwo.SetVisibility("public") + _repoTwo.SetAllowEvents(api.NewEventsFromMask(1)) + _repoTwo.SetPipelineType(constants.PipelineTypeYAML) + _repoTwo.SetTopics([]string{}) + + _build := testutils.APIBuild() + _build.SetID(1) + _build.SetNumber(1) + _build.SetDeployNumber(0) + _build.SetDeployPayload(nil) + _deploymentOne := testutils.APIDeployment() _deploymentOne.SetID(1) - _deploymentOne.SetRepoID(1) + _deploymentOne.SetRepo(_repoOne) _deploymentOne.SetNumber(1) _deploymentOne.SetURL("https://github.com/github/octocat/deployments/1") _deploymentOne.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135163") @@ -36,11 +64,11 @@ func TestDeployment_Engine_ListDeploymentsForRepo(t *testing.T) { _deploymentOne.SetPayload(map[string]string{"foo": "test1"}) _deploymentOne.SetCreatedAt(1) _deploymentOne.SetCreatedBy("octocat") - _deploymentOne.SetBuilds(builds) + _deploymentOne.SetBuilds([]*api.Build{_build}) _deploymentTwo := testutils.APIDeployment() _deploymentTwo.SetID(2) - _deploymentTwo.SetRepoID(2) + _deploymentTwo.SetRepo(_repoTwo) _deploymentTwo.SetNumber(2) _deploymentTwo.SetURL("https://github.com/github/octocat/deployments/2") _deploymentTwo.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135164") @@ -51,7 +79,6 @@ func TestDeployment_Engine_ListDeploymentsForRepo(t *testing.T) { _deploymentTwo.SetPayload(map[string]string{"foo": "test1"}) _deploymentTwo.SetCreatedAt(1) _deploymentTwo.SetCreatedBy("octocat") - _deploymentTwo.SetBuilds(builds) _postgres, _mock := testPostgres(t) defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() @@ -59,49 +86,63 @@ func TestDeployment_Engine_ListDeploymentsForRepo(t *testing.T) { // create expected result in mock _rows := sqlmock.NewRows( []string{"id", "repo_id", "number", "url", "commit", "ref", "task", "target", "description", "payload", "created_at", "created_by", "builds"}). - AddRow(1, 1, 1, "https://github.com/github/octocat/deployments/1", "48afb5bdc41ad69bf22588491333f7cf71135163", "refs/heads/master", "vela-deploy", "production", "Deployment request from Vela", "{\"foo\":\"test1\"}", 1, "octocat", "{}") + AddRow(1, 1, 1, "https://github.com/github/octocat/deployments/1", "48afb5bdc41ad69bf22588491333f7cf71135163", "refs/heads/master", "vela-deploy", "production", "Deployment request from Vela", "{\"foo\":\"test1\"}", 1, "octocat", "{1}") + + _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) + + _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) // ensure the mock expects the query _mock.ExpectQuery(`SELECT * FROM "deployments" WHERE repo_id = $1 ORDER BY number DESC LIMIT $2`).WithArgs(1, 10).WillReturnRows(_rows) + _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) + _mock.ExpectQuery(`SELECT * FROM "builds" WHERE id = $1 LIMIT $2`).WithArgs(1, 1).WillReturnRows(_buildRows) _sqlite := testSqlite(t) defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() - _, err := _sqlite.CreateDeployment(context.TODO(), _deploymentOne) - if err != nil { - t.Errorf("unable to create test schedule for sqlite: %v", err) - } - - _, err = _sqlite.CreateDeployment(context.TODO(), _deploymentTwo) - if err != nil { - t.Errorf("unable to create test deployments for sqlite: %v", err) - } + sqlitePopulateTables( + t, + _sqlite, + []*api.Deployment{_deploymentOne, _deploymentTwo}, + []*api.User{_owner}, + []*api.Repo{_repoOne, _repoTwo}, + []*api.Build{_build}, + ) // setup tests tests := []struct { failure bool name string database *engine - want []*library.Deployment + want []*api.Deployment }{ { failure: false, name: "postgres", database: _postgres, - want: []*library.Deployment{_deploymentOne}, + want: []*api.Deployment{_deploymentOne}, }, { failure: false, name: "sqlite3", database: _sqlite, - want: []*library.Deployment{_deploymentOne}, + want: []*api.Deployment{_deploymentOne}, }, } // run tests for _, test := range tests { t.Run(test.name, func(t *testing.T) { - got, err := test.database.ListDeploymentsForRepo(context.TODO(), _repo, 1, 10) + got, err := test.database.ListDeploymentsForRepo(context.TODO(), _repoOne, 1, 10) if test.failure { if err == nil { @@ -115,8 +156,16 @@ func TestDeployment_Engine_ListDeploymentsForRepo(t *testing.T) { t.Errorf("ListDeploymentsForRepo for %s returned err: %v", test.name, err) } - if !reflect.DeepEqual(got, test.want) { - t.Errorf("ListDeploymentsForRepo for %s is %v, want %v", test.name, got, test.want) + if diff := cmp.Diff( + test.want, + got, + cmp.Options{ + cmp.FilterPath(func(p cmp.Path) bool { + return p.String() == "Builds.Repo.Owner" // ignore this nested struct due to ToAPI + }, cmp.Ignore()), + }, + ); diff != "" { + t.Errorf("GetDeploymentForRepo for %s is a mismatch (-want +got):\n%s", test.name, diff) } }) } diff --git a/database/deployment/list_test.go b/database/deployment/list_test.go index 8edefe64e..c45b8198c 100644 --- a/database/deployment/list_test.go +++ b/database/deployment/list_test.go @@ -4,22 +4,44 @@ package deployment 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 TestDeployment_Engine_ListDeployments(t *testing.T) { - builds := []*library.Build{} - // setup types + _owner := testutils.APIUser().Crop() + _owner.SetID(1) + _owner.SetName("foo") + _owner.SetToken("bar") + + _repoOne := testutils.APIRepo() + _repoOne.SetID(1) + _repoOne.SetOwner(_owner) + _repoOne.SetHash("baz") + _repoOne.SetOrg("foo") + _repoOne.SetName("bar") + _repoOne.SetFullName("foo/bar") + _repoOne.SetVisibility("public") + _repoOne.SetAllowEvents(api.NewEventsFromMask(1)) + _repoOne.SetPipelineType(constants.PipelineTypeYAML) + _repoOne.SetTopics([]string{}) + + _build := testutils.APIBuild() + _build.SetID(1) + _build.SetNumber(1) + _build.SetDeployNumber(0) + _build.SetDeployPayload(nil) + _deploymentOne := testutils.APIDeployment() _deploymentOne.SetID(1) - _deploymentOne.SetRepoID(1) + _deploymentOne.SetRepo(_repoOne) _deploymentOne.SetNumber(1) _deploymentOne.SetURL("https://github.com/github/octocat/deployments/1") _deploymentOne.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135163") @@ -30,11 +52,11 @@ func TestDeployment_Engine_ListDeployments(t *testing.T) { _deploymentOne.SetPayload(map[string]string{"foo": "test1"}) _deploymentOne.SetCreatedAt(1) _deploymentOne.SetCreatedBy("octocat") - _deploymentOne.SetBuilds(builds) + _deploymentOne.SetBuilds([]*api.Build{_build}) _deploymentTwo := testutils.APIDeployment() _deploymentTwo.SetID(2) - _deploymentTwo.SetRepoID(2) + _deploymentTwo.SetRepo(_repoOne) _deploymentTwo.SetNumber(2) _deploymentTwo.SetURL("https://github.com/github/octocat/deployments/2") _deploymentTwo.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135164") @@ -45,7 +67,6 @@ func TestDeployment_Engine_ListDeployments(t *testing.T) { _deploymentTwo.SetPayload(map[string]string{"foo": "test1"}) _deploymentTwo.SetCreatedAt(1) _deploymentTwo.SetCreatedBy("octocat") - _deploymentTwo.SetBuilds(builds) _postgres, _mock := testPostgres(t) defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() @@ -53,43 +74,57 @@ func TestDeployment_Engine_ListDeployments(t *testing.T) { // create expected result in mock _rows := sqlmock.NewRows( []string{"id", "repo_id", "number", "url", "commit", "ref", "task", "target", "description", "payload", "created_at", "created_by", "builds"}). - AddRow(1, 1, 1, "https://github.com/github/octocat/deployments/1", "48afb5bdc41ad69bf22588491333f7cf71135163", "refs/heads/master", "vela-deploy", "production", "Deployment request from Vela", "{\"foo\":\"test1\"}", 1, "octocat", "{}"). - AddRow(2, 2, 2, "https://github.com/github/octocat/deployments/2", "48afb5bdc41ad69bf22588491333f7cf71135164", "refs/heads/master", "vela-deploy", "production", "Deployment request from Vela", "{\"foo\":\"test1\"}", 1, "octocat", "{}") + AddRow(1, 1, 1, "https://github.com/github/octocat/deployments/1", "48afb5bdc41ad69bf22588491333f7cf71135163", "refs/heads/master", "vela-deploy", "production", "Deployment request from Vela", "{\"foo\":\"test1\"}", 1, "octocat", "{1}"). + AddRow(2, 1, 2, "https://github.com/github/octocat/deployments/2", "48afb5bdc41ad69bf22588491333f7cf71135164", "refs/heads/master", "vela-deploy", "production", "Deployment request from Vela", "{\"foo\":\"test1\"}", 1, "octocat", "{}") + + _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) + + _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) // ensure the mock expects the query _mock.ExpectQuery(`SELECT * FROM "deployments"`).WillReturnRows(_rows) + _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) + _mock.ExpectQuery(`SELECT * FROM "builds" WHERE id = $1 LIMIT $2`).WithArgs(1, 1).WillReturnRows(_buildRows) _sqlite := testSqlite(t) defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() - _, err := _sqlite.CreateDeployment(context.TODO(), _deploymentOne) - if err != nil { - t.Errorf("unable to create test deployment for sqlite: %v", err) - } - - _, err = _sqlite.CreateDeployment(context.TODO(), _deploymentTwo) - if err != nil { - t.Errorf("unable to create test deployment for sqlite: %v", err) - } + sqlitePopulateTables( + t, + _sqlite, + []*api.Deployment{_deploymentOne, _deploymentTwo}, + []*api.User{_owner}, + []*api.Repo{_repoOne}, + []*api.Build{_build}, + ) // setup tests tests := []struct { failure bool name string database *engine - want []*library.Deployment + want []*api.Deployment }{ { failure: false, name: "postgres", database: _postgres, - want: []*library.Deployment{_deploymentOne, _deploymentTwo}, + want: []*api.Deployment{_deploymentOne, _deploymentTwo}, }, { failure: false, name: "sqlite3", database: _sqlite, - want: []*library.Deployment{_deploymentOne, _deploymentTwo}, + want: []*api.Deployment{_deploymentOne, _deploymentTwo}, }, } @@ -110,8 +145,16 @@ func TestDeployment_Engine_ListDeployments(t *testing.T) { t.Errorf("ListDeployments for %s returned err: %v", test.name, err) } - if !reflect.DeepEqual(got, test.want) { - t.Errorf("ListDeployments for %s is %v, want %v", test.name, got, test.want) + if diff := cmp.Diff( + test.want, + got, + cmp.Options{ + cmp.FilterPath(func(p cmp.Path) bool { + return p.String() == "Builds.Repo.Owner" // ignore this nested struct due to ToAPI + }, cmp.Ignore()), + }, + ); diff != "" { + t.Errorf("GetDeploymentForRepo for %s is a mismatch (-want +got):\n%s", test.name, diff) } }) } diff --git a/database/deployment/opts.go b/database/deployment/opts.go index 95354e098..373ca53e3 100644 --- a/database/deployment/opts.go +++ b/database/deployment/opts.go @@ -42,6 +42,16 @@ func WithSkipCreation(skipCreation bool) EngineOpt { } } +// WithEncryptionKey sets the encryption key in the database engine for Deployments. +func WithEncryptionKey(key string) EngineOpt { + return func(e *engine) error { + // set the encryption key in the build engine + e.config.EncryptionKey = key + + return nil + } +} + // WithContext sets the context in the database engine for Deployments. func WithContext(ctx context.Context) EngineOpt { return func(e *engine) error { diff --git a/database/deployment/update.go b/database/deployment/update.go index 810450d79..0cb7c8b51 100644 --- a/database/deployment/update.go +++ b/database/deployment/update.go @@ -7,19 +7,19 @@ import ( "github.com/sirupsen/logrus" + 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" ) // UpdateDeployment updates an existing deployment in the database. -func (e *engine) UpdateDeployment(ctx context.Context, d *library.Deployment) (*library.Deployment, error) { +func (e *engine) UpdateDeployment(ctx context.Context, d *api.Deployment) (*api.Deployment, error) { e.logger.WithFields(logrus.Fields{ "deployment": d.GetID(), }).Tracef("updating deployment %d", d.GetID()) // cast the library type to database type - deployment := database.DeploymentFromLibrary(d) + deployment := types.DeploymentFromAPI(d) // validate the necessary fields are populated err := deployment.Validate() @@ -33,5 +33,5 @@ func (e *engine) UpdateDeployment(ctx context.Context, d *library.Deployment) (* Save(deployment) // send query to the database - return deployment.ToLibrary(d.Builds), result.Error + return deployment.ToAPI(d.Builds), result.Error } diff --git a/database/deployment/update_test.go b/database/deployment/update_test.go index d70b3ea85..9f453ee8c 100644 --- a/database/deployment/update_test.go +++ b/database/deployment/update_test.go @@ -9,16 +9,19 @@ import ( "github.com/DATA-DOG/go-sqlmock" "github.com/go-vela/server/database/testutils" - "github.com/go-vela/types/library" ) func TestDeployment_Engine_UpdateDeployment(t *testing.T) { - builds := []*library.Build{} - // setup types + _repoOne := testutils.APIRepo() + _repoOne.SetID(1) + _repoOne.SetOrg("foo") + _repoOne.SetName("bar") + _repoOne.SetFullName("foo/bar") + _deploymentOne := testutils.APIDeployment() _deploymentOne.SetID(1) - _deploymentOne.SetRepoID(1) + _deploymentOne.SetRepo(_repoOne) _deploymentOne.SetNumber(1) _deploymentOne.SetURL("https://github.com/github/octocat/deployments/1") _deploymentOne.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135163") @@ -29,7 +32,6 @@ func TestDeployment_Engine_UpdateDeployment(t *testing.T) { _deploymentOne.SetPayload(map[string]string{"foo": "test1"}) _deploymentOne.SetCreatedAt(1) _deploymentOne.SetCreatedBy("octocat") - _deploymentOne.SetBuilds(builds) _postgres, _mock := testPostgres(t) defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() diff --git a/database/integration_test.go b/database/integration_test.go index d62464b3a..b30295422 100644 --- a/database/integration_test.go +++ b/database/integration_test.go @@ -43,7 +43,7 @@ import ( type Resources struct { Builds []*api.Build Dashboards []*api.Dashboard - Deployments []*library.Deployment + Deployments []*api.Deployment Executables []*library.BuildExecutable Hooks []*api.Hook JWKs jwk.Set @@ -322,12 +322,7 @@ func testBuilds(t *testing.T, db Interface, resources *Resources) { t.Errorf("Number of results for ListBuildsForDashboardRepo() is %v, want %v", len(list), 1) } - // ListBuildsForDashboardRepo does not contain nested repo - wantBuild := *resources.Builds[0] - wantBuild.Repo = testutils.APIRepo() - wantBuild.Repo.Owner = testutils.APIUser().Crop() - - if diff := cmp.Diff([]*api.Build{&wantBuild}, list); diff != "" { + if diff := cmp.Diff([]*api.Build{resources.Hooks[0].GetBuild()}, list); diff != "" { t.Errorf("ListBuildsForDashboardRepo() mismatch (-want +got):\n%s", diff) } methods["ListBuildsForDashboardRepo"] = true @@ -636,6 +631,30 @@ func testDeployments(t *testing.T, db Interface, resources *Resources) { methods[element.Method(i).Name] = false } + // create the users for deployment 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 deployment 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 deployment 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 deployments for _, deployment := range resources.Deployments { _, err := db.CreateDeployment(context.TODO(), deployment) @@ -683,14 +702,14 @@ func testDeployments(t *testing.T, db Interface, resources *Resources) { if int(count) != len(resources.Deployments) { t.Errorf("ListDeploymentsForRepo() is %v, want %v", count, len(resources.Deployments)) } - if diff := cmp.Diff([]*library.Deployment{resources.Deployments[1], resources.Deployments[0]}, list); diff != "" { + if diff := cmp.Diff([]*api.Deployment{resources.Deployments[1], resources.Deployments[0]}, list); diff != "" { t.Errorf("ListDeploymentsForRepo() mismatch (-want +got):\n%s", diff) } methods["ListDeploymentsForRepo"] = true // lookup the deployments by name for _, deployment := range resources.Deployments { - repo := resources.Repos[deployment.GetRepoID()-1] + repo := resources.Repos[deployment.GetRepo().GetID()-1] got, err := db.GetDeploymentForRepo(context.TODO(), repo, deployment.GetNumber()) if err != nil { t.Errorf("unable to get deployment %d for repo %d: %v", deployment.GetID(), repo.GetID(), err) @@ -729,6 +748,30 @@ func testDeployments(t *testing.T, db Interface, resources *Resources) { } methods["DeleteDeployment"] = 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 { @@ -2558,11 +2601,16 @@ func newResources() *Resources { executableTwo.SetBuildID(2) executableTwo.SetData([]byte("foo")) - builds := []*library.Build{} - deploymentOne := new(library.Deployment) + trimmedBuildOne := *buildOne + trimmedBuildOne.Repo = &api.Repo{ID: repoOne.ID} + + trimmedBuildTwo := *buildTwo + trimmedBuildTwo.Repo = &api.Repo{ID: repoOne.ID} + + deploymentOne := new(api.Deployment) deploymentOne.SetID(1) deploymentOne.SetNumber(1) - deploymentOne.SetRepoID(1) + deploymentOne.SetRepo(repoOne) deploymentOne.SetURL("https://github.com/github/octocat/deployments/1") deploymentOne.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135163") deploymentOne.SetRef("refs/heads/main") @@ -2572,12 +2620,12 @@ func newResources() *Resources { deploymentOne.SetPayload(map[string]string{"foo": "test1"}) deploymentOne.SetCreatedAt(1) deploymentOne.SetCreatedBy("octocat") - deploymentOne.SetBuilds(builds) + deploymentOne.SetBuilds([]*api.Build{&trimmedBuildOne}) - deploymentTwo := new(library.Deployment) + deploymentTwo := new(api.Deployment) deploymentTwo.SetID(2) deploymentTwo.SetNumber(2) - deploymentTwo.SetRepoID(1) + deploymentTwo.SetRepo(repoOne) deploymentTwo.SetURL("https://github.com/github/octocat/deployments/2") deploymentTwo.SetCommit("48afb5bdc41ad69bf22588491333f7cf71135164") deploymentTwo.SetRef("refs/heads/main") @@ -2587,18 +2635,11 @@ func newResources() *Resources { deploymentTwo.SetPayload(map[string]string{"foo": "test1"}) deploymentTwo.SetCreatedAt(1) deploymentTwo.SetCreatedBy("octocat") - deploymentTwo.SetBuilds(builds) - - 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.SetRepo(repoOne) - hookOne.SetBuild(&hookBuildOne) + hookOne.SetBuild(&trimmedBuildOne) hookOne.SetNumber(1) hookOne.SetSourceID("c8da1302-07d6-11ea-882f-4893bca275b8") hookOne.SetCreated(time.Now().UTC().Unix()) @@ -2629,7 +2670,7 @@ func newResources() *Resources { hookThree := new(api.Hook) hookThree.SetID(3) hookThree.SetRepo(repoOne) - hookThree.SetBuild(&hookBuildTwo) + hookThree.SetBuild(&trimmedBuildTwo) hookThree.SetNumber(3) hookThree.SetSourceID("c8da1302-07d6-11ea-882f-6793bca275b8") hookThree.SetCreated(time.Now().UTC().Unix()) @@ -2913,7 +2954,7 @@ func newResources() *Resources { return &Resources{ Builds: []*api.Build{buildOne, buildTwo}, Dashboards: []*api.Dashboard{dashboardOne, dashboardTwo}, - Deployments: []*library.Deployment{deploymentOne, deploymentTwo}, + Deployments: []*api.Deployment{deploymentOne, deploymentTwo}, Executables: []*library.BuildExecutable{executableOne, executableTwo}, Hooks: []*api.Hook{hookOne, hookTwo, hookThree}, JWKs: jwkSet, diff --git a/database/resource.go b/database/resource.go index d3705e571..c17dc0f0b 100644 --- a/database/resource.go +++ b/database/resource.go @@ -81,6 +81,7 @@ func (e *engine) NewResources(ctx context.Context) error { deployment.WithClient(e.client), deployment.WithLogger(e.logger), deployment.WithSkipCreation(e.config.SkipCreation), + deployment.WithEncryptionKey(e.config.EncryptionKey), ) if err != nil { return err diff --git a/database/testutils/api_resources.go b/database/testutils/api_resources.go index be1fa9d72..821526141 100644 --- a/database/testutils/api_resources.go +++ b/database/testutils/api_resources.go @@ -58,12 +58,12 @@ func APIBuild() *api.Build { } } -func APIDeployment() *library.Deployment { - builds := []*library.Build{} +func APIDeployment() *api.Deployment { + builds := []*api.Build{} - return &library.Deployment{ + return &api.Deployment{ ID: new(int64), - RepoID: new(int64), + Repo: APIRepo(), Number: new(int64), URL: new(string), Commit: new(string), diff --git a/database/types/build.go b/database/types/build.go index 66157607e..146c3dc5a 100644 --- a/database/types/build.go +++ b/database/types/build.go @@ -283,8 +283,17 @@ func (b *Build) Nullify() *Build { func (b *Build) ToAPI() *api.Build { build := new(api.Build) + // set Repo based on presence of repo data + var repo *api.Repo + if b.Repo.ID.Valid { + repo = b.Repo.ToAPI() + } else { + repo = new(api.Repo) + repo.SetID(b.RepoID.Int64) + } + build.SetID(b.ID.Int64) - build.SetRepo(b.Repo.ToAPI()) + build.SetRepo(repo) build.SetPipelineID(b.PipelineID.Int64) build.SetNumber(int(b.Number.Int32)) build.SetParent(int(b.Parent.Int32)) diff --git a/database/types/deployment.go b/database/types/deployment.go new file mode 100644 index 000000000..ec3453651 --- /dev/null +++ b/database/types/deployment.go @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: Apache-2.0 + +package types + +import ( + "database/sql" + "errors" + "fmt" + + "github.com/lib/pq" + + api "github.com/go-vela/server/api/types" + "github.com/go-vela/server/util" + "github.com/go-vela/types/constants" + "github.com/go-vela/types/raw" +) + +var ( + // ErrEmptyDeploymentNumber defines the error type when a + // Deployment type has an empty Number field provided. + ErrEmptyDeploymentNumber = errors.New("empty deployment number provided") + + // ErrEmptyDeploymentRepoID defines the error type when a + // Deployment type has an empty RepoID field provided. + ErrEmptyDeploymentRepoID = errors.New("empty deployment repo_id provided") +) + +// Deployment is the database representation of a deployment for a repo. +type Deployment struct { + ID sql.NullInt64 `sql:"id"` + Number sql.NullInt64 `sql:"number"` + RepoID sql.NullInt64 `sql:"repo_id"` + URL sql.NullString `sql:"url"` + Commit sql.NullString `sql:"commit"` + Ref sql.NullString `sql:"ref"` + Task sql.NullString `sql:"task"` + Target sql.NullString `sql:"target"` + Description sql.NullString `sql:"description"` + Payload raw.StringSliceMap `sql:"payload"` + CreatedAt sql.NullInt64 `sql:"created_at"` + CreatedBy sql.NullString `sql:"created_by"` + Builds pq.StringArray `sql:"builds" gorm:"type:varchar(50)"` + + Repo Repo `gorm:"foreignKey:RepoID"` +} + +// Nullify ensures the valid flag for +// the sql.Null types are properly set. +// +// When a field within the Deployment type is the zero +// value for the field, the valid flag is set to +// false causing it to be NULL in the database. +func (d *Deployment) Nullify() *Deployment { + if d == nil { + return nil + } + + // check if the ID field should be false + if d.ID.Int64 == 0 { + d.ID.Valid = false + } + + // check if the Number field should be false + if d.Number.Int64 == 0 { + d.Number.Valid = false + } + + // check if the RepoID field should be false + if d.RepoID.Int64 == 0 { + d.RepoID.Valid = false + } + + // check if the URL field should be false + if len(d.URL.String) == 0 { + d.URL.Valid = false + } + + // check if the Commit field should be false + if len(d.Commit.String) == 0 { + d.Commit.Valid = false + } + + // check if the Ref field should be false + if len(d.Ref.String) == 0 { + d.Ref.Valid = false + } + + // check if the Task field should be false + if len(d.Task.String) == 0 { + d.Task.Valid = false + } + + // check if the Target field should be false + if len(d.Target.String) == 0 { + d.Target.Valid = false + } + + // check if the Description field should be false + if len(d.Description.String) == 0 { + d.Description.Valid = false + } + + // check if the CreatedAt field should be false + if d.CreatedAt.Int64 == 0 { + d.CreatedAt.Valid = false + } + + // check if the CreatedBy field should be false + if len(d.CreatedBy.String) == 0 { + d.CreatedBy.Valid = false + } + + return d +} + +// ToAPI converts the Deployment type +// to the API Deployment type. +func (d *Deployment) ToAPI(builds []*api.Build) *api.Deployment { + deployment := new(api.Deployment) + + deployment.SetID(d.ID.Int64) + deployment.SetNumber(d.Number.Int64) + deployment.SetRepo(d.Repo.ToAPI()) + deployment.SetURL(d.URL.String) + deployment.SetCommit(d.Commit.String) + deployment.SetRef(d.Ref.String) + deployment.SetTask(d.Task.String) + deployment.SetTarget(d.Target.String) + deployment.SetDescription(d.Description.String) + deployment.SetPayload(d.Payload) + deployment.SetCreatedAt(d.CreatedAt.Int64) + deployment.SetCreatedBy(d.CreatedBy.String) + + if len(builds) > 0 { + deployment.SetBuilds(builds) + } + + return deployment +} + +// Validate verifies the necessary fields for +// the Deployment type are populated correctly. +func (d *Deployment) Validate() error { + // verify the RepoID field is populated + if d.RepoID.Int64 <= 0 { + return ErrEmptyDeploymentRepoID + } + + // verify the Number field is populated + if d.Number.Int64 <= 0 { + return ErrEmptyDeploymentNumber + } + + // ensure that all Deployment string fields + // that can be returned as JSON are sanitized + // to avoid unsafe HTML content + d.Commit = sql.NullString{String: util.Sanitize(d.Commit.String), Valid: d.Commit.Valid} + d.Ref = sql.NullString{String: util.Sanitize(d.Ref.String), Valid: d.Ref.Valid} + d.Task = sql.NullString{String: util.Sanitize(d.Task.String), Valid: d.Task.Valid} + d.Target = sql.NullString{String: util.Sanitize(d.Target.String), Valid: d.Target.Valid} + d.Description = sql.NullString{String: util.Sanitize(d.Description.String), Valid: d.Description.Valid} + + // calculate total size of builds + total := 0 + for _, b := range d.Builds { + total += len(b) + } + + // verify the Builds field is within the database constraints and evict if not + // len is to factor in number of comma separators included in the database field, + // removing 1 due to the last item not having an appended comma + if diff := (total + len(d.Builds) - 1) - constants.DeployBuildsMaxSize; diff > 0 { + for diff > 0 { + diff = diff - (len(d.Builds[0]) + 1) + d.Builds = d.Builds[1:] + } + } + + return nil +} + +// DeploymentFromAPI converts the API Deployment type +// to a database Deployment type. +func DeploymentFromAPI(d *api.Deployment) *Deployment { + buildIDs := []string{} + for _, build := range d.GetBuilds() { + buildIDs = append(buildIDs, fmt.Sprint(build.GetID())) + } + + deployment := &Deployment{ + ID: sql.NullInt64{Int64: d.GetID(), Valid: true}, + Number: sql.NullInt64{Int64: d.GetNumber(), Valid: true}, + RepoID: sql.NullInt64{Int64: d.GetRepo().GetID(), Valid: true}, + URL: sql.NullString{String: d.GetURL(), Valid: true}, + Commit: sql.NullString{String: d.GetCommit(), Valid: true}, + Ref: sql.NullString{String: d.GetRef(), Valid: true}, + Task: sql.NullString{String: d.GetTask(), Valid: true}, + Target: sql.NullString{String: d.GetTarget(), Valid: true}, + Description: sql.NullString{String: d.GetDescription(), Valid: true}, + Payload: d.GetPayload(), + CreatedAt: sql.NullInt64{Int64: d.GetCreatedAt(), Valid: true}, + CreatedBy: sql.NullString{String: d.GetCreatedBy(), Valid: true}, + Builds: buildIDs, + } + + return deployment.Nullify() +} diff --git a/database/types/deployment_test.go b/database/types/deployment_test.go new file mode 100644 index 000000000..b9289047f --- /dev/null +++ b/database/types/deployment_test.go @@ -0,0 +1,241 @@ +// SPDX-License-Identifier: Apache-2.0 + +package types + +import ( + "database/sql" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/lib/pq" + + api "github.com/go-vela/server/api/types" + "github.com/go-vela/types/raw" +) + +func TestDatabase_Deployment_Nullify(t *testing.T) { + // setup types + var d *Deployment + + want := &Deployment{ + ID: sql.NullInt64{Int64: 0, Valid: false}, + Number: sql.NullInt64{Int64: 0, Valid: false}, + RepoID: sql.NullInt64{Int64: 0, Valid: false}, + URL: sql.NullString{String: "", Valid: false}, + Commit: sql.NullString{String: "", Valid: false}, + Ref: sql.NullString{String: "", Valid: false}, + Task: sql.NullString{String: "", Valid: false}, + Target: sql.NullString{String: "", Valid: false}, + Description: sql.NullString{String: "", Valid: false}, + Payload: nil, + CreatedAt: sql.NullInt64{Int64: 0, Valid: false}, + CreatedBy: sql.NullString{String: "", Valid: false}, + Builds: nil, + } + + // setup tests + tests := []struct { + deployment *Deployment + want *Deployment + }{ + { + deployment: testDeployment(), + want: testDeployment(), + }, + { + deployment: d, + want: nil, + }, + { + deployment: new(Deployment), + want: want, + }, + } + + // run tests + for _, test := range tests { + got := test.deployment.Nullify() + + if diff := cmp.Diff(test.want, got); diff != "" { + t.Errorf("(ToAPI: -want +got):\n%s", diff) + } + } +} + +func TestDatabase_Deployment_ToAPI(t *testing.T) { + builds := []*api.Build{testBuild().ToAPI()} + + want := new(api.Deployment) + want.SetID(1) + want.SetNumber(1) + want.SetRepo(testRepo().ToAPI()) + want.SetURL("https://github.com/github/octocat/deployments/1") + want.SetCommit("1234") + want.SetRef("refs/heads/main") + want.SetTask("deploy:vela") + want.SetTarget("production") + want.SetDescription("Deployment request from Vela") + want.SetPayload(raw.StringSliceMap{"foo": "test1"}) + want.SetCreatedAt(1) + want.SetCreatedBy("octocat") + want.SetBuilds(builds) + + got := testDeployment().ToAPI(builds) + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("(ToAPI: -want +got):\n%s", diff) + } +} + +func TestDatabase_Deployment_Validate(t *testing.T) { + // setup types + tests := []struct { + failure bool + deployment *Deployment + want *Deployment + }{ + { + failure: false, + deployment: testDeployment(), + }, + { // no number set for deployment + failure: true, + deployment: &Deployment{ + ID: sql.NullInt64{Int64: 1, Valid: true}, + RepoID: sql.NullInt64{Int64: 1, Valid: true}, + }, + want: &Deployment{ + ID: sql.NullInt64{Int64: 1, Valid: true}, + RepoID: sql.NullInt64{Int64: 1, Valid: true}, + }, + }, + { // no repoID set for deployment + failure: true, + deployment: &Deployment{ + ID: sql.NullInt64{Int64: 1, Valid: true}, + Number: sql.NullInt64{Int64: 1, Valid: true}, + }, + want: &Deployment{ + ID: sql.NullInt64{Int64: 1, Valid: true}, + RepoID: sql.NullInt64{Int64: 1, Valid: true}, + }, + }, + { // too many builds + failure: true, + deployment: &Deployment{ + ID: sql.NullInt64{Int64: 1, Valid: true}, + Number: sql.NullInt64{Int64: 1, Valid: true}, + Builds: generateBuilds(100), + }, + want: &Deployment{ + ID: sql.NullInt64{Int64: 1, Valid: true}, + RepoID: sql.NullInt64{Int64: 1, Valid: true}, + Builds: generateBuilds(50), + }, + }, + { // acceptable builds + failure: true, + deployment: &Deployment{ + ID: sql.NullInt64{Int64: 1, Valid: true}, + Number: sql.NullInt64{Int64: 1, Valid: true}, + Builds: generateBuilds(30), + }, + want: &Deployment{ + ID: sql.NullInt64{Int64: 1, Valid: true}, + RepoID: sql.NullInt64{Int64: 1, Valid: true}, + Builds: generateBuilds(30), + }, + }, + } + + // run tests + for _, test := range tests { + err := test.deployment.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 TestDatabase_DeploymentFromAPI(t *testing.T) { + builds := []*api.Build{testBuild().ToAPI()} + + d := new(api.Deployment) + d.SetID(1) + d.SetNumber(1) + d.SetRepo(testRepo().ToAPI()) + d.SetURL("https://github.com/github/octocat/deployments/1") + d.SetCommit("1234") + d.SetRef("refs/heads/main") + d.SetTask("deploy:vela") + d.SetTarget("production") + d.SetDescription("Deployment request from Vela") + d.SetPayload(raw.StringSliceMap{"foo": "test1"}) + d.SetCreatedAt(1) + d.SetCreatedBy("octocat") + d.SetBuilds(builds) + + want := &Deployment{ + ID: sql.NullInt64{Int64: 1, Valid: true}, + Number: sql.NullInt64{Int64: 1, Valid: true}, + RepoID: sql.NullInt64{Int64: 1, Valid: true}, + URL: sql.NullString{String: "https://github.com/github/octocat/deployments/1", Valid: true}, + Commit: sql.NullString{String: "1234", Valid: true}, + Ref: sql.NullString{String: "refs/heads/main", Valid: true}, + Task: sql.NullString{String: "deploy:vela", Valid: true}, + Target: sql.NullString{String: "production", Valid: true}, + Description: sql.NullString{String: "Deployment request from Vela", Valid: true}, + Payload: raw.StringSliceMap{"foo": "test1"}, + CreatedAt: sql.NullInt64{Int64: 1, Valid: true}, + CreatedBy: sql.NullString{String: "octocat", Valid: true}, + Builds: pq.StringArray{"1"}, + } + + // run test + got := DeploymentFromAPI(d) + + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("(-want +got):\n%s", diff) + } +} + +// testDeployment is a test helper function to create a Deployment type with all fields set to a fake value. +func testDeployment() *Deployment { + return &Deployment{ + ID: sql.NullInt64{Int64: 1, Valid: true}, + Number: sql.NullInt64{Int64: 1, Valid: true}, + RepoID: sql.NullInt64{Int64: 1, Valid: true}, + URL: sql.NullString{String: "https://github.com/github/octocat/deployments/1", Valid: true}, + Commit: sql.NullString{String: "1234", Valid: true}, + Ref: sql.NullString{String: "refs/heads/main", Valid: true}, + Task: sql.NullString{String: "deploy:vela", Valid: true}, + Target: sql.NullString{String: "production", Valid: true}, + Description: sql.NullString{String: "Deployment request from Vela", Valid: true}, + Payload: raw.StringSliceMap{"foo": "test1"}, + CreatedAt: sql.NullInt64{Int64: 1, Valid: true}, + CreatedBy: sql.NullString{String: "octocat", Valid: true}, + Builds: pq.StringArray{"1"}, + + Repo: *testRepo(), + } +} + +// generateBuilds returns a list of valid builds that exceed the maximum size. +func generateBuilds(amount int) []string { + // initialize empty builds + builds := []string{} + + for i := 0; i < amount; i++ { + builds = append(builds, "123456789") + } + + return builds +} diff --git a/database/types/hook.go b/database/types/hook.go index c76422535..239055b2e 100644 --- a/database/types/hook.go +++ b/database/types/hook.go @@ -138,15 +138,8 @@ func (h *Hook) Nullify() *Hook { 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) + if h.BuildID.Int64 > 0 { + hook.SetBuild(h.Build.ToAPI()) } hook.SetID(h.ID.Int64) diff --git a/internal/webhook.go b/internal/webhook.go index 4e38ab185..3c6889a59 100644 --- a/internal/webhook.go +++ b/internal/webhook.go @@ -7,7 +7,6 @@ import ( api "github.com/go-vela/server/api/types" "github.com/go-vela/types/constants" - "github.com/go-vela/types/library" ) var ( @@ -31,7 +30,7 @@ type Webhook struct { Repo *api.Repo Build *api.Build PullRequest PullRequest - Deployment *library.Deployment + Deployment *api.Deployment } // ShouldSkip uses the build information diff --git a/mock/server/deployment.go b/mock/server/deployment.go index e68e24d07..19323ec14 100644 --- a/mock/server/deployment.go +++ b/mock/server/deployment.go @@ -10,52 +10,414 @@ 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 ( // DeploymentResp represents a JSON return for a single build. DeploymentResp = `{ "id": 1, - "number": 1234, - "repo_id": 1, - "url": "https://api.github.com/repos/github/octocat/deployments/1", - "commit": "48afb5bdc41ad69bf22588491333f7cf71135163", + "number": 744479, + "repo": { + "id": 1, + "owner": { + "id": 1, + "name": "Octocat", + "active": true + }, + "org": "Octocat", + "name": "myvela", + "full_name": "Octocat/myvela", + "link": "https://github.com/Octocat/myvela", + "clone": "https://github.com/Octocat/myvela.git", + "branch": "main", + "topics": [ + "example" + ], + "build_limit": 10, + "timeout": 30, + "counter": 2, + "visibility": "public", + "private": false, + "trusted": false, + "active": true, + "allow_events": { + "push": { + "branch": true, + "tag": false, + "delete_branch": false, + "delete_tag": false + }, + "pull_request": { + "opened": false, + "edited": false, + "synchronize": false, + "reopened": false, + "labeled": false, + "unlabeled": false + }, + "deployment": { + "created": true + }, + "comment": { + "created": false, + "edited": false + }, + "schedule": { + "run": false + } + }, + "pipeline_type": "yaml", + "previous_name": "", + "approve_build": "first-time" + }, + "url": "https://github.com/api/v3/repos/Octocat/myvela/deployments/744479", + "commit": "14c8b131c0c5e1489811bd64c755a9b6f74c792b", "ref": "main", "task": "deploy:vela", "target": "production", "description": "Deployment request from Vela", "payload": {}, - "created_at": 1, - "created_by": "octocat", - "builds": [] + "created_at": 1727710527, + "created_by": "Octocat", + "builds": [ + { + "id": 1, + "repo": { + "id": 1 + }, + "pipeline_id": 1, + "number": 1, + "parent": 0, + "event": "deployment", + "event_action": "created", + "status": "success", + "error": "", + "enqueued": 1727710528, + "created": 1727710528, + "started": 1727710528, + "finished": 1727710532, + "deploy": "production", + "deploy_number": 744479, + "deploy_payload": {}, + "clone": "https://github.com/Octocat/myvela.git", + "source": "https://github.com/api/v3/repos/Octocat/myvela/deployments/744479", + "title": "deployment received from https://github.com/Octocat/myvela", + "message": "Deployment request from Vela", + "commit": "14c8b131c0c5e1489811bd64c755a9b6f74c792b", + "sender": "Octocat", + "sender_scm_id": "17043", + "author": "Octocat", + "email": "", + "link": "http://localhost:8888/Octocat/myvela/1", + "branch": "main", + "ref": "refs/heads/main", + "base_ref": "", + "head_ref": "", + "host": "worker", + "runtime": "docker", + "distribution": "linux", + "approved_at": 0, + "approved_by": "" + }, + { + "id": 2, + "repo": { + "id": 1 + }, + "pipeline_id": 1, + "number": 2, + "parent": 0, + "event": "deployment", + "event_action": "created", + "status": "success", + "error": "", + "enqueued": 1727711899, + "created": 1727711899, + "started": 1727711899, + "finished": 1727711904, + "deploy": "production", + "deploy_number": 744479, + "deploy_payload": {}, + "clone": "https://github.com/Octocat/myvela.git", + "source": "https://github.com/api/v3/repos/Octocat/myvela/deployments/744479", + "title": "deployment received from https://github.com/Octocat/myvela", + "message": "Deployment request from Vela", + "commit": "14c8b131c0c5e1489811bd64c755a9b6f74c792b", + "sender": "Octocat", + "sender_scm_id": "17043", + "author": "Octocat", + "email": "", + "link": "http://localhost:8888/Octocat/myvela/2", + "branch": "main", + "ref": "refs/heads/main", + "base_ref": "", + "head_ref": "", + "host": "worker", + "runtime": "docker", + "distribution": "linux", + "approved_at": 0, + "approved_by": "" + } + ] }` // DeploymentsResp represents a JSON return for one to many builds. DeploymentsResp = `[ - { - "id": 2, - "repo_id": 1, - "url": "https://api.github.com/repos/github/octocat/deployments/2", - "user": "octocat", - "commit": "48afb5bdc41ad69bf22588491333f7cf71135163", - "ref": "main", - "task": "deploy:vela", - "target": "production", - "description": "Deployment request from Vela" +{ + "id": 1, + "number": 744479, + "repo": { + "id": 1, + "owner": { + "id": 1, + "name": "Octocat", + "active": true + }, + "org": "Octocat", + "name": "myvela", + "full_name": "Octocat/myvela", + "link": "https://github.com/Octocat/myvela", + "clone": "https://github.com/Octocat/myvela.git", + "branch": "main", + "topics": [ + "example" + ], + "build_limit": 10, + "timeout": 30, + "counter": 2, + "visibility": "public", + "private": false, + "trusted": false, + "active": true, + "allow_events": { + "push": { + "branch": true, + "tag": false, + "delete_branch": false, + "delete_tag": false + }, + "pull_request": { + "opened": false, + "edited": false, + "synchronize": false, + "reopened": false, + "labeled": false, + "unlabeled": false + }, + "deployment": { + "created": true + }, + "comment": { + "created": false, + "edited": false + }, + "schedule": { + "run": false + } + }, + "pipeline_type": "yaml", + "previous_name": "", + "approve_build": "first-time" }, - { + "url": "https://github.com/api/v3/repos/Octocat/myvela/deployments/744479", + "commit": "14c8b131c0c5e1489811bd64c755a9b6f74c792b", + "ref": "main", + "task": "deploy:vela", + "target": "production", + "description": "Deployment request from Vela", + "payload": {}, + "created_at": 1727710527, + "created_by": "Octocat", + "builds": [ + { + "id": 1, + "repo": { + "id": 1 + }, + "pipeline_id": 1, + "number": 1, + "parent": 0, + "event": "deployment", + "event_action": "created", + "status": "success", + "error": "", + "enqueued": 1727710528, + "created": 1727710528, + "started": 1727710528, + "finished": 1727710532, + "deploy": "production", + "deploy_number": 744479, + "deploy_payload": {}, + "clone": "https://github.com/Octocat/myvela.git", + "source": "https://github.com/api/v3/repos/Octocat/myvela/deployments/744479", + "title": "deployment received from https://github.com/Octocat/myvela", + "message": "Deployment request from Vela", + "commit": "14c8b131c0c5e1489811bd64c755a9b6f74c792b", + "sender": "Octocat", + "sender_scm_id": "17043", + "author": "Octocat", + "email": "", + "link": "http://localhost:8888/Octocat/myvela/1", + "branch": "main", + "ref": "refs/heads/main", + "base_ref": "", + "head_ref": "", + "host": "worker", + "runtime": "docker", + "distribution": "linux", + "approved_at": 0, + "approved_by": "" + }, + { + "id": 2, + "repo": { + "id": 1 + }, + "pipeline_id": 1, + "number": 2, + "parent": 0, + "event": "deployment", + "event_action": "created", + "status": "success", + "error": "", + "enqueued": 1727711899, + "created": 1727711899, + "started": 1727711899, + "finished": 1727711904, + "deploy": "production", + "deploy_number": 744479, + "deploy_payload": {}, + "clone": "https://github.com/Octocat/myvela.git", + "source": "https://github.com/api/v3/repos/Octocat/myvela/deployments/744479", + "title": "deployment received from https://github.com/Octocat/myvela", + "message": "Deployment request from Vela", + "commit": "14c8b131c0c5e1489811bd64c755a9b6f74c792b", + "sender": "Octocat", + "sender_scm_id": "17043", + "author": "Octocat", + "email": "", + "link": "http://localhost:8888/Octocat/myvela/2", + "branch": "main", + "ref": "refs/heads/main", + "base_ref": "", + "head_ref": "", + "host": "worker", + "runtime": "docker", + "distribution": "linux", + "approved_at": 0, + "approved_by": "" + } + ] +}, +{ + "id": 1, + "number": 744479, + "repo": { "id": 1, - "repo_id": 1, - "url": "https://api.github.com/repos/github/octocat/deployments/1", - "user": "octocat", - "commit": "48afb5bdc41ad69bf22588491333f7cf71135163", - "ref": "main", - "task": "deploy:vela", - "target": "production", - "description": "Deployment request from Vela" - } + "owner": { + "id": 1, + "name": "Octocat", + "active": true + }, + "org": "Octocat", + "name": "myvela", + "full_name": "Octocat/myvela", + "link": "https://github.com/Octocat/myvela", + "clone": "https://github.com/Octocat/myvela.git", + "branch": "main", + "topics": [ + "example" + ], + "build_limit": 10, + "timeout": 30, + "counter": 2, + "visibility": "public", + "private": false, + "trusted": false, + "active": true, + "allow_events": { + "push": { + "branch": true, + "tag": false, + "delete_branch": false, + "delete_tag": false + }, + "pull_request": { + "opened": false, + "edited": false, + "synchronize": false, + "reopened": false, + "labeled": false, + "unlabeled": false + }, + "deployment": { + "created": true + }, + "comment": { + "created": false, + "edited": false + }, + "schedule": { + "run": false + } + }, + "pipeline_type": "yaml", + "previous_name": "", + "approve_build": "first-time" + }, + "url": "https://github.com/api/v3/repos/Octocat/myvela/deployments/744479", + "commit": "14c8b131c0c5e1489811bd64c755a9b6f74c792b", + "ref": "main", + "task": "deploy:vela", + "target": "production", + "description": "Deployment request from Vela", + "payload": {}, + "created_at": 1727710527, + "created_by": "Octocat", + "builds": [ + { + "id": 2, + "repo": { + "id": 1 + }, + "pipeline_id": 1, + "number": 2, + "parent": 0, + "event": "deployment", + "event_action": "created", + "status": "success", + "error": "", + "enqueued": 1727711899, + "created": 1727711899, + "started": 1727711899, + "finished": 1727711904, + "deploy": "production", + "deploy_number": 744479, + "deploy_payload": {}, + "clone": "https://github.com/Octocat/myvela.git", + "source": "https://github.com/api/v3/repos/Octocat/myvela/deployments/744479", + "title": "deployment received from https://github.com/Octocat/myvela", + "message": "Deployment request from Vela", + "commit": "14c8b131c0c5e1489811bd64c755a9b6f74c792b", + "sender": "Octocat", + "sender_scm_id": "17043", + "author": "Octocat", + "email": "", + "link": "http://localhost:8888/Octocat/myvela/2", + "branch": "main", + "ref": "refs/heads/main", + "base_ref": "", + "head_ref": "", + "host": "worker", + "runtime": "docker", + "distribution": "linux", + "approved_at": 0, + "approved_by": "" + } + ] +} ]` ) @@ -63,7 +425,7 @@ const ( func getDeployments(c *gin.Context) { data := []byte(DeploymentsResp) - var body []library.Deployment + var body []api.Deployment _ = json.Unmarshal(data, &body) c.JSON(http.StatusOK, body) @@ -83,7 +445,7 @@ func getDeployment(c *gin.Context) { data := []byte(DeploymentResp) - var body library.Deployment + var body api.Deployment _ = json.Unmarshal(data, &body) c.JSON(http.StatusOK, body) @@ -93,7 +455,7 @@ func getDeployment(c *gin.Context) { func addDeployment(c *gin.Context) { data := []byte(DeploymentResp) - var body library.Deployment + var body api.Deployment _ = json.Unmarshal(data, &body) c.JSON(http.StatusCreated, body) @@ -103,7 +465,7 @@ func addDeployment(c *gin.Context) { func updateDeployment(c *gin.Context) { data := []byte(DeploymentResp) - var body library.Deployment + var body api.Deployment _ = json.Unmarshal(data, &body) c.JSON(http.StatusOK, body) diff --git a/mock/server/deployment_test.go b/mock/server/deployment_test.go index 0796c566c..fd5862e11 100644 --- a/mock/server/deployment_test.go +++ b/mock/server/deployment_test.go @@ -7,11 +7,11 @@ import ( "reflect" "testing" - "github.com/go-vela/types/library" + api "github.com/go-vela/server/api/types" ) func TestDeployment_ActiveDeploymentResp(t *testing.T) { - testDeployment := library.Deployment{} + testDeployment := api.Deployment{} err := json.Unmarshal([]byte(DeploymentResp), &testDeployment) if err != nil { diff --git a/scm/github/deployment.go b/scm/github/deployment.go index 399c18c96..85f377494 100644 --- a/scm/github/deployment.go +++ b/scm/github/deployment.go @@ -10,12 +10,11 @@ import ( "github.com/sirupsen/logrus" api "github.com/go-vela/server/api/types" - "github.com/go-vela/types/library" "github.com/go-vela/types/raw" ) // GetDeployment gets a deployment from the GitHub repo. -func (c *client) GetDeployment(ctx context.Context, u *api.User, r *api.Repo, id int64) (*library.Deployment, error) { +func (c *client) GetDeployment(ctx context.Context, u *api.User, r *api.Repo, id int64) (*api.Deployment, error) { c.Logger.WithFields(logrus.Fields{ "org": r.GetOrg(), "repo": r.GetName(), @@ -40,9 +39,9 @@ func (c *client) GetDeployment(ctx context.Context, u *api.User, r *api.Repo, id createdAt := deployment.CreatedAt.Unix() - return &library.Deployment{ + return &api.Deployment{ ID: deployment.ID, - RepoID: r.ID, + Repo: r, URL: deployment.URL, Commit: deployment.SHA, Ref: deployment.Ref, @@ -98,7 +97,7 @@ func (c *client) GetDeploymentCount(ctx context.Context, u *api.User, r *api.Rep } // GetDeploymentList gets a list of deployments from the GitHub repo. -func (c *client) GetDeploymentList(ctx context.Context, u *api.User, r *api.Repo, page, perPage int) ([]*library.Deployment, error) { +func (c *client) GetDeploymentList(ctx context.Context, u *api.User, r *api.Repo, page, perPage int) ([]*api.Deployment, error) { c.Logger.WithFields(logrus.Fields{ "org": r.GetOrg(), "repo": r.GetName(), @@ -123,7 +122,7 @@ func (c *client) GetDeploymentList(ctx context.Context, u *api.User, r *api.Repo } // variable we want to return - deployments := []*library.Deployment{} + deployments := []*api.Deployment{} // iterate through all API results for _, deployment := range d { @@ -137,9 +136,9 @@ func (c *client) GetDeploymentList(ctx context.Context, u *api.User, r *api.Repo createdAt := deployment.CreatedAt.Unix() // convert query result to library type - deployments = append(deployments, &library.Deployment{ + deployments = append(deployments, &api.Deployment{ ID: deployment.ID, - RepoID: r.ID, + Repo: r, URL: deployment.URL, Commit: deployment.SHA, Ref: deployment.Ref, @@ -156,7 +155,7 @@ func (c *client) GetDeploymentList(ctx context.Context, u *api.User, r *api.Repo } // CreateDeployment creates a new deployment for the GitHub repo. -func (c *client) CreateDeployment(ctx context.Context, u *api.User, r *api.Repo, d *library.Deployment) error { +func (c *client) CreateDeployment(ctx context.Context, u *api.User, r *api.Repo, d *api.Deployment) error { c.Logger.WithFields(logrus.Fields{ "org": r.GetOrg(), "repo": r.GetName(), @@ -192,7 +191,7 @@ func (c *client) CreateDeployment(ctx context.Context, u *api.User, r *api.Repo, } d.SetNumber(deploy.GetID()) - d.SetRepoID(r.GetID()) + d.SetRepo(r) d.SetURL(deploy.GetURL()) d.SetCommit(deploy.GetSHA()) d.SetRef(deploy.GetRef()) diff --git a/scm/github/deployment_test.go b/scm/github/deployment_test.go index f985194ee..b09c1ba50 100644 --- a/scm/github/deployment_test.go +++ b/scm/github/deployment_test.go @@ -11,7 +11,6 @@ import ( "github.com/gin-gonic/gin" api "github.com/go-vela/server/api/types" - "github.com/go-vela/types/library" ) func TestGithub_CreateDeployment(t *testing.T) { @@ -42,9 +41,9 @@ func TestGithub_CreateDeployment(t *testing.T) { r.SetName("bar") r.SetFullName("foo/bar") - d := new(library.Deployment) + d := new(api.Deployment) d.SetID(1) - d.SetRepoID(1) + d.SetRepo(r) d.SetURL("https://api.github.com/repos/foo/bar/deployments/1") d.SetCommit("a84d88e7554fc1fa21bcbc4efae3c782a70d2b9d") d.SetRef("topic-branch") diff --git a/scm/github/webhook.go b/scm/github/webhook.go index e2f44819b..d79deb718 100644 --- a/scm/github/webhook.go +++ b/scm/github/webhook.go @@ -19,7 +19,6 @@ import ( api "github.com/go-vela/server/api/types" "github.com/go-vela/server/internal" "github.com/go-vela/types/constants" - "github.com/go-vela/types/library" ) // ProcessWebhook parses the webhook from a repo. @@ -386,7 +385,7 @@ func (c *client) processDeploymentEvent(h *api.Hook, payload *github.DeploymentE b.SetBranch(payload.GetDeployment().GetRef()) b.SetRef(payload.GetDeployment().GetRef()) - d := new(library.Deployment) + d := new(api.Deployment) d.SetNumber(payload.GetDeployment().GetID()) d.SetURL(payload.GetDeployment().GetURL()) diff --git a/scm/github/webhook_test.go b/scm/github/webhook_test.go index 23c87be17..e53ab0b05 100644 --- a/scm/github/webhook_test.go +++ b/scm/github/webhook_test.go @@ -18,7 +18,6 @@ import ( api "github.com/go-vela/server/api/types" "github.com/go-vela/server/internal" "github.com/go-vela/types/constants" - "github.com/go-vela/types/library" "github.com/go-vela/types/raw" ) @@ -613,7 +612,7 @@ func TestGithub_ProcessWebhook_Deployment(t *testing.T) { wantBuild.SetBranch("main") wantBuild.SetRef("refs/heads/main") - wantDeployment := new(library.Deployment) + wantDeployment := new(api.Deployment) wantDeployment.SetNumber(145988746) wantDeployment.SetURL("https://api.github.com/repos/Codertocat/Hello-World/deployments/145988746") wantDeployment.SetCommit("f95f852bd8fca8fcc58a9a2d6c842781e32a215e") @@ -630,7 +629,7 @@ func TestGithub_ProcessWebhook_Deployment(t *testing.T) { repo *api.Repo build *api.Build deploymentPayload raw.StringSliceMap - deployment *library.Deployment + deployment *api.Deployment } tests := []struct { @@ -749,7 +748,7 @@ func TestGithub_ProcessWebhook_Deployment_Commit(t *testing.T) { wantBuild.SetBranch("main") wantBuild.SetRef("refs/heads/main") - wantDeployment := new(library.Deployment) + wantDeployment := new(api.Deployment) wantDeployment.SetNumber(145988746) wantDeployment.SetURL("https://api.github.com/repos/Codertocat/Hello-World/deployments/145988746") wantDeployment.SetCommit("f95f852bd8fca8fcc58a9a2d6c842781e32a215e") diff --git a/scm/service.go b/scm/service.go index 697bcf8ef..ca93ec1d5 100644 --- a/scm/service.go +++ b/scm/service.go @@ -85,16 +85,16 @@ type Service interface { // GetDeployment defines a function that // gets a deployment by number and repo. - GetDeployment(context.Context, *api.User, *api.Repo, int64) (*library.Deployment, error) + GetDeployment(context.Context, *api.User, *api.Repo, int64) (*api.Deployment, error) // GetDeploymentCount defines a function that // counts a list of all deployment for a repo. GetDeploymentCount(context.Context, *api.User, *api.Repo) (int64, error) // GetDeploymentList defines a function that gets // a list of all deployments for a repo. - GetDeploymentList(context.Context, *api.User, *api.Repo, int, int) ([]*library.Deployment, error) + GetDeploymentList(context.Context, *api.User, *api.Repo, int, int) ([]*api.Deployment, error) // CreateDeployment defines a function that // creates a new deployment. - CreateDeployment(context.Context, *api.User, *api.Repo, *library.Deployment) error + CreateDeployment(context.Context, *api.User, *api.Repo, *api.Deployment) error // Repo SCM Interface Functions From 5afe31d436d78ac7dcc7b0add4ba24954229e458 Mon Sep 17 00:00:00 2001 From: ecrupper Date: Mon, 30 Sep 2024 11:53:41 -0500 Subject: [PATCH 2/3] gci --- database/deployment/deployment_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/database/deployment/deployment_test.go b/database/deployment/deployment_test.go index cba36347c..b3df02069 100644 --- a/database/deployment/deployment_test.go +++ b/database/deployment/deployment_test.go @@ -8,13 +8,14 @@ import ( "testing" "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/types" "github.com/sirupsen/logrus" "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 TestDeployment_New(t *testing.T) { From a752a4cf586f9a5859e670e4e01b8cf2851fbe7b Mon Sep 17 00:00:00 2001 From: ecrupper Date: Mon, 30 Sep 2024 13:50:44 -0500 Subject: [PATCH 3/3] fix tests --- database/build/list_dashboard_test.go | 23 +++++++---------------- database/deployment/get_repo_test.go | 4 ++++ database/deployment/list_repo_test.go | 4 ++++ database/deployment/list_test.go | 4 ++++ database/testutils/api_resources.go | 3 --- database/types/hook_test.go | 6 +----- 6 files changed, 20 insertions(+), 24 deletions(-) diff --git a/database/build/list_dashboard_test.go b/database/build/list_dashboard_test.go index 803c19db2..5b0473284 100644 --- a/database/build/list_dashboard_test.go +++ b/database/build/list_dashboard_test.go @@ -15,12 +15,12 @@ import ( func TestBuild_Engine_ListBuildsForDashboardRepo(t *testing.T) { // setup types - _repo := testutils.APIRepo() - _repo.SetID(1) + _repoBuild := new(api.Repo) + _repoBuild.SetID(1) _buildOne := testutils.APIBuild() _buildOne.SetID(1) - _buildOne.SetRepo(_repo) + _buildOne.SetRepo(_repoBuild) _buildOne.SetNumber(1) _buildOne.SetDeployPayload(nil) _buildOne.SetCreated(1) @@ -29,22 +29,13 @@ func TestBuild_Engine_ListBuildsForDashboardRepo(t *testing.T) { _buildTwo := testutils.APIBuild() _buildTwo.SetID(2) - _buildTwo.SetRepo(_repo) + _buildTwo.SetRepo(_repoBuild) _buildTwo.SetNumber(2) _buildTwo.SetDeployPayload(nil) _buildTwo.SetCreated(2) _buildTwo.SetEvent("pull_request") _buildTwo.SetBranch("main") - // ListBuildsForDashboardRepo does not return the repo object but the repo ID is needed to create builds - _wantBuildOne := *_buildOne - _wantBuildOne.Repo = testutils.APIRepo() - _wantBuildOne.Repo.Owner = testutils.APIUser().Crop() - - _wantBuildTwo := *_buildTwo - _wantBuildTwo.Repo = testutils.APIRepo() - _wantBuildTwo.Repo.Owner = testutils.APIUser().Crop() - _postgres, _mock := testPostgres(t) defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() @@ -81,20 +72,20 @@ func TestBuild_Engine_ListBuildsForDashboardRepo(t *testing.T) { failure: false, name: "postgres", database: _postgres, - want: []*api.Build{&_wantBuildTwo, &_wantBuildOne}, + want: []*api.Build{_buildTwo, _buildOne}, }, { failure: false, name: "sqlite3", database: _sqlite, - want: []*api.Build{&_wantBuildTwo, &_wantBuildOne}, + want: []*api.Build{_buildTwo, _buildOne}, }, } // run tests for _, test := range tests { t.Run(test.name, func(t *testing.T) { - got, err := test.database.ListBuildsForDashboardRepo(context.TODO(), _repo, []string{"main"}, []string{"push", "pull_request"}) + got, err := test.database.ListBuildsForDashboardRepo(context.TODO(), _repoBuild, []string{"main"}, []string{"push", "pull_request"}) if test.failure { if err == nil { diff --git a/database/deployment/get_repo_test.go b/database/deployment/get_repo_test.go index 3bd86ab29..e79de6407 100644 --- a/database/deployment/get_repo_test.go +++ b/database/deployment/get_repo_test.go @@ -33,8 +33,12 @@ func TestDeployment_Engine_GetDeploymentForRepo(t *testing.T) { _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) diff --git a/database/deployment/list_repo_test.go b/database/deployment/list_repo_test.go index 777de2ba4..76b3fa1ea 100644 --- a/database/deployment/list_repo_test.go +++ b/database/deployment/list_repo_test.go @@ -45,8 +45,12 @@ func TestDeployment_Engine_ListDeploymentsForRepo(t *testing.T) { _repoTwo.SetPipelineType(constants.PipelineTypeYAML) _repoTwo.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) diff --git a/database/deployment/list_test.go b/database/deployment/list_test.go index c45b8198c..14e9577ea 100644 --- a/database/deployment/list_test.go +++ b/database/deployment/list_test.go @@ -33,8 +33,12 @@ func TestDeployment_Engine_ListDeployments(t *testing.T) { _repoOne.SetPipelineType(constants.PipelineTypeYAML) _repoOne.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) diff --git a/database/testutils/api_resources.go b/database/testutils/api_resources.go index 821526141..71f14e703 100644 --- a/database/testutils/api_resources.go +++ b/database/testutils/api_resources.go @@ -59,8 +59,6 @@ func APIBuild() *api.Build { } func APIDeployment() *api.Deployment { - builds := []*api.Build{} - return &api.Deployment{ ID: new(int64), Repo: APIRepo(), @@ -74,7 +72,6 @@ func APIDeployment() *api.Deployment { Payload: new(raw.StringSliceMap), CreatedAt: new(int64), CreatedBy: new(string), - Builds: builds, } } diff --git a/database/types/hook_test.go b/database/types/hook_test.go index e9b16dcea..d8770c749 100644 --- a/database/types/hook_test.go +++ b/database/types/hook_test.go @@ -79,11 +79,7 @@ func TestTypes_Hook_ToAPI(t *testing.T) { 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) + want.SetBuild(testBuild().ToAPI()) // run test got := testHook().ToAPI()