diff --git a/api/admin/build.go b/api/admin/build.go index 28ca14ddf..71a343e88 100644 --- a/api/admin/build.go +++ b/api/admin/build.go @@ -56,7 +56,7 @@ func AllBuildsQueue(c *gin.Context) { after := c.DefaultQuery("after", strconv.FormatInt(time.Now().UTC().Add(-24*time.Hour).Unix(), 10)) // send API call to capture pending and running builds - b, err := database.FromContext(c).GetPendingAndRunningBuilds(after) + b, err := database.FromContext(c).ListPendingAndRunningBuilds(after) if err != nil { retErr := fmt.Errorf("unable to capture all running and pending builds: %w", err) diff --git a/api/badge.go b/api/badge.go index 799de69ab..cdc028e28 100644 --- a/api/badge.go +++ b/api/badge.go @@ -57,7 +57,7 @@ func GetBadge(c *gin.Context) { }).Infof("creating latest build badge for repo %s on branch %s", r.GetFullName(), branch) // send API call to capture the last build for the repo and branch - b, err := database.FromContext(c).GetLastBuildByBranch(r, branch) + b, err := database.FromContext(c).LastBuildForRepo(r, branch) if err != nil { c.String(http.StatusOK, constants.BadgeUnknown) return diff --git a/api/build.go b/api/build.go index 445d4cc8a..cd6c8b86f 100644 --- a/api/build.go +++ b/api/build.go @@ -144,7 +144,7 @@ func CreateBuild(c *gin.Context) { } // send API call to capture the number of pending or running builds for the repo - builds, err := database.FromContext(c).GetRepoBuildCount(r, filters) + builds, err := database.FromContext(c).CountBuildsForRepo(r, filters) if err != nil { retErr := fmt.Errorf("unable to create new build: unable to get count of builds for repo %s", r.GetFullName()) @@ -358,7 +358,7 @@ func CreateBuild(c *gin.Context) { } // send API call to capture the created build - input, _ = database.FromContext(c).GetBuild(input.GetNumber(), r) + input, _ = database.FromContext(c).GetBuildForRepo(r, input.GetNumber()) c.JSON(http.StatusCreated, input) @@ -472,7 +472,7 @@ func GetBuildByID(c *gin.Context) { }).Infof("reading build %d", id) // Get build from database - b, err = database.FromContext(c).GetBuildByID(id) + b, err = database.FromContext(c).GetBuild(id) if err != nil { retErr := fmt.Errorf("unable to get build: %w", err) @@ -725,7 +725,7 @@ func GetBuilds(c *gin.Context) { return } - b, t, err = database.FromContext(c).GetRepoBuildList(r, filters, before, after, page, perPage) + b, t, err = database.FromContext(c).ListBuildsForRepo(r, filters, before, after, page, perPage) if err != nil { retErr := fmt.Errorf("unable to get builds for repo %s: %w", r.GetFullName(), err) @@ -898,7 +898,7 @@ func GetOrgBuilds(c *gin.Context) { } // send API call to capture the list of builds for the org (and event type if passed in) - b, t, err = database.FromContext(c).GetOrgBuildList(o, filters, page, perPage) + b, t, err = database.FromContext(c).ListBuildsForOrg(o, filters, page, perPage) if err != nil { retErr := fmt.Errorf("unable to get builds for org %s: %w", o, err) @@ -1063,7 +1063,7 @@ func RestartBuild(c *gin.Context) { } // send API call to capture the number of pending or running builds for the repo - builds, err := database.FromContext(c).GetRepoBuildCount(r, filters) + builds, err := database.FromContext(c).CountBuildsForRepo(r, filters) if err != nil { retErr := fmt.Errorf("unable to restart build: unable to get count of builds for repo %s", r.GetFullName()) @@ -1287,7 +1287,7 @@ func RestartBuild(c *gin.Context) { } // send API call to capture the restarted build - b, _ = database.FromContext(c).GetBuild(b.GetNumber(), r) + b, _ = database.FromContext(c).GetBuildForRepo(r, b.GetNumber()) c.JSON(http.StatusCreated, b) @@ -1448,7 +1448,7 @@ func UpdateBuild(c *gin.Context) { } // send API call to capture the updated build - b, _ = database.FromContext(c).GetBuild(b.GetNumber(), r) + b, _ = database.FromContext(c).GetBuildForRepo(r, b.GetNumber()) c.JSON(http.StatusOK, b) @@ -1533,7 +1533,7 @@ func DeleteBuild(c *gin.Context) { }).Infof("deleting build %s", entry) // send API call to remove the build - err := database.FromContext(c).DeleteBuild(b.GetID()) + err := database.FromContext(c).DeleteBuild(b) if err != nil { retErr := fmt.Errorf("unable to delete build %s: %w", entry, err) @@ -1594,7 +1594,7 @@ func PlanBuild(database database.Interface, p *pipeline.Build, b *library.Build, // send API call to capture the created build // TODO: this can be dropped once we return // the created build above - b, err = database.GetBuild(b.GetNumber(), r) + b, err = database.GetBuildForRepo(r, b.GetNumber()) if err != nil { return fmt.Errorf("unable to get new build for %s: %w", r.GetFullName(), err) } diff --git a/api/deployment/list.go b/api/deployment/list.go index bc5f9e97a..b3745aeca 100644 --- a/api/deployment/list.go +++ b/api/deployment/list.go @@ -138,7 +138,7 @@ func ListDeployments(c *gin.Context) { dWithBs := []*library.Deployment{} for _, deployment := range d { - b, err := database.FromContext(c).GetDeploymentBuildList(*deployment.URL) + b, _, err := database.FromContext(c).ListBuildsForDeployment(deployment, nil, 1, 3) if err != nil { retErr := fmt.Errorf("unable to get builds for deployment %d: %w", deployment.GetID(), err) diff --git a/api/metrics.go b/api/metrics.go index d68ab35e7..d005481b3 100644 --- a/api/metrics.go +++ b/api/metrics.go @@ -236,7 +236,7 @@ func recordGauges(c *gin.Context) { // build_count if q.BuildCount { // send API call to capture the total number of builds - b, err := database.FromContext(c).GetBuildCount() + b, err := database.FromContext(c).CountBuilds() if err != nil { logrus.Errorf("unable to get count of all builds: %v", err) } @@ -247,7 +247,7 @@ func recordGauges(c *gin.Context) { // running_build_count if q.RunningBuildCount { // send API call to capture the total number of running builds - bRun, err := database.FromContext(c).GetBuildCountByStatus("running") + bRun, err := database.FromContext(c).CountBuildsForStatus("running", nil) if err != nil { logrus.Errorf("unable to get count of all running builds: %v", err) } @@ -258,7 +258,7 @@ func recordGauges(c *gin.Context) { // pending_build_count if q.PendingBuildCount { // send API call to capture the total number of pending builds - bPen, err := database.FromContext(c).GetBuildCountByStatus("pending") + bPen, err := database.FromContext(c).CountBuildsForStatus("pending", nil) if err != nil { logrus.Errorf("unable to get count of all pending builds: %v", err) } @@ -280,7 +280,7 @@ func recordGauges(c *gin.Context) { // failure_build_count if q.FailureBuildCount { // send API call to capture the total number of failure builds - bFail, err := database.FromContext(c).GetBuildCountByStatus("failure") + bFail, err := database.FromContext(c).CountBuildsForStatus("failure", nil) if err != nil { logrus.Errorf("unable to get count of all failure builds: %v", err) } @@ -291,7 +291,7 @@ func recordGauges(c *gin.Context) { // killed_build_count if q.KilledBuildCount { // send API call to capture the total number of killed builds - bKill, err := database.FromContext(c).GetBuildCountByStatus("killed") + bKill, err := database.FromContext(c).CountBuildsForStatus("killed", nil) if err != nil { logrus.Errorf("unable to get count of all killed builds: %v", err) } @@ -302,7 +302,7 @@ func recordGauges(c *gin.Context) { // success_build_count if q.SuccessBuildCount { // send API call to capture the total number of success builds - bSucc, err := database.FromContext(c).GetBuildCountByStatus("success") + bSucc, err := database.FromContext(c).CountBuildsForStatus("success", nil) if err != nil { logrus.Errorf("unable to get count of all success builds: %v", err) } @@ -313,7 +313,7 @@ func recordGauges(c *gin.Context) { // error_build_count if q.ErrorBuildCount { // send API call to capture the total number of error builds - bErr, err := database.FromContext(c).GetBuildCountByStatus("error") + bErr, err := database.FromContext(c).CountBuildsForStatus("error", nil) if err != nil { logrus.Errorf("unable to get count of all error builds: %v", err) } diff --git a/api/webhook.go b/api/webhook.go index 5f4ddc5bf..6881a03b7 100644 --- a/api/webhook.go +++ b/api/webhook.go @@ -15,18 +15,16 @@ import ( "strings" "time" + "github.com/gin-gonic/gin" "github.com/go-vela/server/compiler" "github.com/go-vela/server/database" "github.com/go-vela/server/queue" "github.com/go-vela/server/scm" "github.com/go-vela/server/util" - "github.com/go-vela/types" "github.com/go-vela/types/constants" "github.com/go-vela/types/library" "github.com/go-vela/types/pipeline" - - "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" ) @@ -318,7 +316,7 @@ func PostWebhook(c *gin.Context) { } // send API call to capture the number of pending or running builds for the repo - builds, err := database.FromContext(c).GetRepoBuildCount(repo, filters) + builds, err := database.FromContext(c).CountBuildsForRepo(repo, filters) if err != nil { retErr := fmt.Errorf("%s: unable to get count of builds for repo %s", baseErr, repo.GetFullName()) util.HandleError(c, http.StatusBadRequest, retErr) @@ -673,7 +671,7 @@ func PostWebhook(c *gin.Context) { } // send API call to capture the triggered build - b, err = database.FromContext(c).GetBuild(b.GetNumber(), repo) + b, err = database.FromContext(c).GetBuildForRepo(repo, b.GetNumber()) if err != nil { retErr := fmt.Errorf("%s: failed to get new build %s/%d: %w", baseErr, repo.GetFullName(), b.GetNumber(), err) util.HandleError(c, http.StatusInternalServerError, retErr) @@ -942,7 +940,7 @@ func renameRepository(h *library.Hook, r *library.Repo, c *gin.Context, m *types } // get total number of builds associated with repository - t, err = database.FromContext(c).GetRepoBuildCount(dbR, nil) + t, err = database.FromContext(c).CountBuildsForRepo(dbR, nil) if err != nil { return nil, fmt.Errorf("unable to get build count for repo %s: %w", dbR.GetFullName(), err) } @@ -951,7 +949,7 @@ func renameRepository(h *library.Hook, r *library.Repo, c *gin.Context, m *types page = 1 // capture all builds belonging to repo in database for build := int64(0); build < t; build += 100 { - b, _, err := database.FromContext(c).GetRepoBuildList(dbR, nil, time.Now().Unix(), 0, page, 100) + b, _, err := database.FromContext(c).ListBuildsForRepo(dbR, nil, time.Now().Unix(), 0, page, 100) if err != nil { return nil, fmt.Errorf("unable to get build list for repo %s: %w", dbR.GetFullName(), err) } diff --git a/cmd/vela-server/schedule.go b/cmd/vela-server/schedule.go index 172b5c251..cb607c59b 100644 --- a/cmd/vela-server/schedule.go +++ b/cmd/vela-server/schedule.go @@ -141,7 +141,7 @@ func processSchedule(s *library.Schedule, compiler compiler.Engine, database dat } // send API call to capture the number of pending or running builds for the repo - builds, err := database.GetRepoBuildCount(r, filters) + builds, err := database.CountBuildsForRepo(r, filters) if err != nil { return fmt.Errorf("unable to get count of builds for repo %s: %w", r.GetFullName(), err) } @@ -362,7 +362,7 @@ func processSchedule(s *library.Schedule, compiler compiler.Engine, database dat } // send API call to capture the triggered build - b, err = database.GetBuild(b.GetNumber(), r) + b, err = database.GetBuildForRepo(r, b.GetNumber()) if err != nil { return fmt.Errorf("unable to get new build %s/%d: %w", r.GetFullName(), b.GetNumber(), err) } diff --git a/database/build/build.go b/database/build/build.go new file mode 100644 index 000000000..6deb13892 --- /dev/null +++ b/database/build/build.go @@ -0,0 +1,80 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "fmt" + + "github.com/go-vela/types/constants" + "github.com/sirupsen/logrus" + + "gorm.io/gorm" +) + +type ( + // config represents the settings required to create the engine that implements the BuildInterface interface. + config struct { + // specifies to skip creating tables and indexes for the Build engine + SkipCreation bool + } + + // engine represents the build functionality that implements the BuildInterface interface. + engine struct { + // engine configuration settings used in build functions + config *config + + // gorm.io/gorm database client used in build functions + // + // https://pkg.go.dev/gorm.io/gorm#DB + client *gorm.DB + + // sirupsen/logrus logger used in build functions + // + // https://pkg.go.dev/github.com/sirupsen/logrus#Entry + logger *logrus.Entry + } +) + +// New creates and returns a Vela service for integrating with builds in the database. +// +//nolint:revive // ignore returning unexported engine +func New(opts ...EngineOpt) (*engine, error) { + // create new Build engine + e := new(engine) + + // create new fields + e.client = new(gorm.DB) + e.config = new(config) + e.logger = new(logrus.Entry) + + // apply all provided configuration options + for _, opt := range opts { + err := opt(e) + if err != nil { + return nil, err + } + } + + // check if we should skip creating build database objects + if e.config.SkipCreation { + e.logger.Warning("skipping creation of builds table and indexes in the database") + + return e, nil + } + + // create the builds table + err := e.CreateBuildTable(e.client.Config.Dialector.Name()) + if err != nil { + return nil, fmt.Errorf("unable to create %s table: %w", constants.TableBuild, err) + } + + // create the indexes for the builds table + err = e.CreateBuildIndexes() + if err != nil { + return nil, fmt.Errorf("unable to create indexes for %s table: %w", constants.TableBuild, err) + } + + return e, nil +} diff --git a/database/build/build_test.go b/database/build/build_test.go new file mode 100644 index 000000000..6304cd167 --- /dev/null +++ b/database/build/build_test.go @@ -0,0 +1,268 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "database/sql/driver" + "reflect" + "testing" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/go-vela/types/library" + "github.com/sirupsen/logrus" + + "gorm.io/driver/postgres" + "gorm.io/driver/sqlite" + "gorm.io/gorm" +) + +func TestBuild_New(t *testing.T) { + // setup types + logger := logrus.NewEntry(logrus.StandardLogger()) + + _sql, _mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) + if err != nil { + t.Errorf("unable to create new SQL mock: %v", err) + } + defer _sql.Close() + + _mock.ExpectExec(CreatePostgresTable).WillReturnResult(sqlmock.NewResult(1, 1)) + _mock.ExpectExec(CreateCreatedIndex).WillReturnResult(sqlmock.NewResult(1, 1)) + _mock.ExpectExec(CreateRepoIDIndex).WillReturnResult(sqlmock.NewResult(1, 1)) + _mock.ExpectExec(CreateSourceIndex).WillReturnResult(sqlmock.NewResult(1, 1)) + _mock.ExpectExec(CreateStatusIndex).WillReturnResult(sqlmock.NewResult(1, 1)) + + _config := &gorm.Config{SkipDefaultTransaction: true} + + _postgres, err := gorm.Open(postgres.New(postgres.Config{Conn: _sql}), _config) + if err != nil { + t.Errorf("unable to create new postgres database: %v", err) + } + + _sqlite, err := gorm.Open(sqlite.Open("file::memory:?cache=shared"), _config) + if err != nil { + t.Errorf("unable to create new sqlite database: %v", err) + } + + defer func() { _sql, _ := _sqlite.DB(); _sql.Close() }() + + // setup tests + tests := []struct { + failure bool + name string + client *gorm.DB + logger *logrus.Entry + skipCreation bool + want *engine + }{ + { + failure: false, + name: "postgres", + client: _postgres, + logger: logger, + skipCreation: false, + want: &engine{ + client: _postgres, + config: &config{SkipCreation: false}, + logger: logger, + }, + }, + { + failure: false, + name: "sqlite3", + client: _sqlite, + logger: logger, + skipCreation: false, + want: &engine{ + client: _sqlite, + config: &config{SkipCreation: false}, + logger: logger, + }, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, err := New( + WithClient(test.client), + WithLogger(test.logger), + WithSkipCreation(test.skipCreation), + ) + + if test.failure { + if err == nil { + t.Errorf("New for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("New for %s returned err: %v", test.name, err) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("New for %s is %v, want %v", test.name, got, test.want) + } + }) + } +} + +// testPostgres is a helper function to create a Postgres engine for testing. +func testPostgres(t *testing.T) (*engine, sqlmock.Sqlmock) { + // create the new mock sql database + // + // https://pkg.go.dev/github.com/DATA-DOG/go-sqlmock#New + _sql, _mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) + if err != nil { + t.Errorf("unable to create new SQL mock: %v", err) + } + + _mock.ExpectExec(CreatePostgresTable).WillReturnResult(sqlmock.NewResult(1, 1)) + _mock.ExpectExec(CreateCreatedIndex).WillReturnResult(sqlmock.NewResult(1, 1)) + _mock.ExpectExec(CreateRepoIDIndex).WillReturnResult(sqlmock.NewResult(1, 1)) + _mock.ExpectExec(CreateSourceIndex).WillReturnResult(sqlmock.NewResult(1, 1)) + _mock.ExpectExec(CreateStatusIndex).WillReturnResult(sqlmock.NewResult(1, 1)) + + // create the new mock Postgres database client + // + // https://pkg.go.dev/gorm.io/gorm#Open + _postgres, err := gorm.Open( + postgres.New(postgres.Config{Conn: _sql}), + &gorm.Config{SkipDefaultTransaction: true}, + ) + if err != nil { + t.Errorf("unable to create new postgres database: %v", err) + } + + _engine, err := New( + WithClient(_postgres), + WithLogger(logrus.NewEntry(logrus.StandardLogger())), + WithSkipCreation(false), + ) + if err != nil { + t.Errorf("unable to create new postgres build engine: %v", err) + } + + return _engine, _mock +} + +// testSqlite is a helper function to create a Sqlite engine for testing. +func testSqlite(t *testing.T) *engine { + _sqlite, err := gorm.Open( + sqlite.Open("file::memory:?cache=shared"), + &gorm.Config{SkipDefaultTransaction: true}, + ) + if err != nil { + t.Errorf("unable to create new sqlite database: %v", err) + } + + _engine, err := New( + WithClient(_sqlite), + WithLogger(logrus.NewEntry(logrus.StandardLogger())), + WithSkipCreation(false), + ) + if err != nil { + t.Errorf("unable to create new sqlite build engine: %v", err) + } + + return _engine +} + +// testBuild is a test helper function to create a library +// Build type with all fields set to their zero values. +func testBuild() *library.Build { + return &library.Build{ + ID: new(int64), + RepoID: new(int64), + PipelineID: new(int64), + Number: new(int), + Parent: new(int), + Event: new(string), + EventAction: new(string), + Status: new(string), + Error: new(string), + Enqueued: new(int64), + Created: new(int64), + Started: new(int64), + Finished: new(int64), + Deploy: new(string), + Clone: new(string), + Source: new(string), + Title: new(string), + Message: new(string), + Commit: new(string), + Sender: new(string), + Author: new(string), + Email: new(string), + Link: new(string), + Branch: new(string), + Ref: new(string), + BaseRef: new(string), + HeadRef: new(string), + Host: new(string), + Runtime: new(string), + Distribution: new(string), + } +} + +// testDeployment is a test helper function to create a library +// Repo type with all fields set to their zero values. +func testDeployment() *library.Deployment { + return &library.Deployment{ + ID: new(int64), + RepoID: new(int64), + URL: new(string), + User: new(string), + Commit: new(string), + Ref: new(string), + Task: new(string), + Target: new(string), + Description: new(string), + } +} + +// testRepo is a test helper function to create a library +// Repo type with all fields set to their zero values. +func testRepo() *library.Repo { + return &library.Repo{ + ID: new(int64), + UserID: new(int64), + BuildLimit: new(int64), + Timeout: new(int64), + Counter: new(int), + PipelineType: new(string), + Hash: new(string), + Org: new(string), + Name: new(string), + FullName: new(string), + Link: new(string), + Clone: new(string), + Branch: new(string), + Visibility: new(string), + PreviousName: new(string), + Private: new(bool), + Trusted: new(bool), + Active: new(bool), + AllowPull: new(bool), + AllowPush: new(bool), + AllowDeploy: new(bool), + AllowTag: new(bool), + AllowComment: new(bool), + } +} + +// This will be used with the github.com/DATA-DOG/go-sqlmock library to compare values +// that are otherwise not easily compared. These typically would be values generated +// before adding or updating them in the database. +// +// https://github.com/DATA-DOG/go-sqlmock#matching-arguments-like-timetime +type AnyArgument struct{} + +// Match satisfies sqlmock.Argument interface. +func (a AnyArgument) Match(v driver.Value) bool { + return true +} diff --git a/database/build/count.go b/database/build/count.go new file mode 100644 index 000000000..7700c5786 --- /dev/null +++ b/database/build/count.go @@ -0,0 +1,25 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "github.com/go-vela/types/constants" +) + +// CountBuilds gets the count of all builds from the database. +func (e *engine) CountBuilds() (int64, error) { + e.logger.Tracef("getting count of all builds from the database") + + // variable to store query results + var b int64 + + // send query to the database and store result in variable + err := e.client. + Table(constants.TableBuild). + Count(&b). + Error + + return b, err +} diff --git a/database/build/count_deployment.go b/database/build/count_deployment.go new file mode 100644 index 000000000..6203f196b --- /dev/null +++ b/database/build/count_deployment.go @@ -0,0 +1,32 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "github.com/go-vela/types/constants" + "github.com/go-vela/types/library" + "github.com/sirupsen/logrus" +) + +// CountBuildsForDeployment gets the count of builds by deployment URL from the database. +func (e *engine) CountBuildsForDeployment(d *library.Deployment, filters map[string]interface{}) (int64, error) { + e.logger.WithFields(logrus.Fields{ + "deployment": d.GetURL(), + }).Tracef("getting count of builds for deployment %s from the database", d.GetURL()) + + // variable to store query results + var b int64 + + // send query to the database and store result in variable + err := e.client. + Table(constants.TableBuild). + Where("source = ?", d.GetURL()). + Where(filters). + Order("number DESC"). + Count(&b). + Error + + return b, err +} diff --git a/database/build/count_deployment_test.go b/database/build/count_deployment_test.go new file mode 100644 index 000000000..53c0c1df6 --- /dev/null +++ b/database/build/count_deployment_test.go @@ -0,0 +1,102 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "reflect" + "testing" + + "github.com/DATA-DOG/go-sqlmock" +) + +func TestBuild_Engine_CountBuildsForDeployment(t *testing.T) { + // setup types + _buildOne := testBuild() + _buildOne.SetID(1) + _buildOne.SetRepoID(1) + _buildOne.SetNumber(1) + _buildOne.SetDeployPayload(nil) + _buildOne.SetSource("https://github.com/github/octocat/deployments/1") + + _buildTwo := testBuild() + _buildTwo.SetID(2) + _buildTwo.SetRepoID(1) + _buildTwo.SetNumber(2) + _buildTwo.SetDeployPayload(nil) + _buildTwo.SetSource("https://github.com/github/octocat/deployments/1") + + _deployment := testDeployment() + _deployment.SetID(1) + _deployment.SetRepoID(1) + _deployment.SetURL("https://github.com/github/octocat/deployments/1") + + _postgres, _mock := testPostgres(t) + defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() + + // create expected result in mock + _rows := sqlmock.NewRows([]string{"count"}).AddRow(2) + + // ensure the mock expects the query + _mock.ExpectQuery(`SELECT count(*) FROM "builds" WHERE source = $1`).WithArgs("https://github.com/github/octocat/deployments/1").WillReturnRows(_rows) + + _sqlite := testSqlite(t) + defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() + + err := _sqlite.CreateBuild(_buildOne) + if err != nil { + t.Errorf("unable to create test build for sqlite: %v", err) + } + + err = _sqlite.CreateBuild(_buildTwo) + if err != nil { + t.Errorf("unable to create test build for sqlite: %v", err) + } + + // setup tests + tests := []struct { + failure bool + name string + database *engine + want int64 + }{ + { + failure: false, + name: "postgres", + database: _postgres, + want: 2, + }, + { + failure: false, + name: "sqlite3", + database: _sqlite, + want: 2, + }, + } + + filters := map[string]interface{}{} + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, err := test.database.CountBuildsForDeployment(_deployment, filters) + + if test.failure { + if err == nil { + t.Errorf("CountBuildsForDeployment for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("CountBuildsForDeployment for %s returned err: %v", test.name, err) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("CountBuildsForDeployment for %s is %v, want %v", test.name, got, test.want) + } + }) + } +} diff --git a/database/build/count_org.go b/database/build/count_org.go new file mode 100644 index 000000000..37825bad6 --- /dev/null +++ b/database/build/count_org.go @@ -0,0 +1,31 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "github.com/go-vela/types/constants" + "github.com/sirupsen/logrus" +) + +// CountBuildsForOrg gets the count of builds by org name from the database. +func (e *engine) CountBuildsForOrg(org string, filters map[string]interface{}) (int64, error) { + e.logger.WithFields(logrus.Fields{ + "org": org, + }).Tracef("getting count of builds for org %s from the database", org) + + // variable to store query results + var b int64 + + // send query to the database and store result in variable + err := e.client. + Table(constants.TableBuild). + Joins("JOIN repos ON builds.repo_id = repos.id"). + Where("repos.org = ?", org). + Where(filters). + Count(&b). + Error + + return b, err +} diff --git a/database/build/count_org_test.go b/database/build/count_org_test.go new file mode 100644 index 000000000..429149e54 --- /dev/null +++ b/database/build/count_org_test.go @@ -0,0 +1,159 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "reflect" + "testing" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/go-vela/types/constants" + "github.com/go-vela/types/database" +) + +func TestBuild_Engine_CountBuildsForOrg(t *testing.T) { + // setup types + _buildOne := testBuild() + _buildOne.SetID(1) + _buildOne.SetRepoID(1) + _buildOne.SetNumber(1) + _buildOne.SetDeployPayload(nil) + _buildOne.SetEvent("push") + + _buildTwo := testBuild() + _buildTwo.SetID(2) + _buildTwo.SetRepoID(2) + _buildTwo.SetNumber(2) + _buildTwo.SetDeployPayload(nil) + _buildTwo.SetEvent("push") + + _repoOne := testRepo() + _repoOne.SetID(1) + _repoOne.SetUserID(1) + _repoOne.SetHash("baz") + _repoOne.SetOrg("foo") + _repoOne.SetName("bar") + _repoOne.SetFullName("foo/bar") + _repoOne.SetVisibility("public") + _repoOne.SetPipelineType("yaml") + _repoOne.SetTopics([]string{}) + + _repoTwo := testRepo() + _repoTwo.SetID(2) + _repoTwo.SetUserID(1) + _repoTwo.SetHash("bar") + _repoTwo.SetOrg("foo") + _repoTwo.SetName("baz") + _repoTwo.SetFullName("foo/baz") + _repoTwo.SetVisibility("public") + _repoTwo.SetPipelineType("yaml") + _repoTwo.SetTopics([]string{}) + + _postgres, _mock := testPostgres(t) + defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() + + // create expected result without filters in mock + _rows := sqlmock.NewRows([]string{"count"}).AddRow(2) + // ensure the mock expects the query without filters + _mock.ExpectQuery(`SELECT count(*) FROM "builds" JOIN repos ON builds.repo_id = repos.id WHERE repos.org = $1`).WithArgs("foo").WillReturnRows(_rows) + + // create expected result with event filter in mock + _rows = sqlmock.NewRows([]string{"count"}).AddRow(2) + // ensure the mock expects the query with event filter + _mock.ExpectQuery(`SELECT count(*) FROM "builds" JOIN repos ON builds.repo_id = repos.id WHERE repos.org = $1 AND "event" = $2`).WithArgs("foo", "push").WillReturnRows(_rows) + + _sqlite := testSqlite(t) + defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() + + err := _sqlite.CreateBuild(_buildOne) + if err != nil { + t.Errorf("unable to create test build for sqlite: %v", err) + } + + err = _sqlite.CreateBuild(_buildTwo) + if err != nil { + t.Errorf("unable to create test build for sqlite: %v", err) + } + + err = _sqlite.client.AutoMigrate(&database.Repo{}) + if err != nil { + t.Errorf("unable to create repo table for sqlite: %v", err) + } + + err = _sqlite.client.Table(constants.TableRepo).Create(database.RepoFromLibrary(_repoOne)).Error + if err != nil { + t.Errorf("unable to create test repo for sqlite: %v", err) + } + + err = _sqlite.client.Table(constants.TableRepo).Create(database.RepoFromLibrary(_repoTwo)).Error + if err != nil { + t.Errorf("unable to create test repo for sqlite: %v", err) + } + + // setup tests + tests := []struct { + failure bool + name string + database *engine + filters map[string]interface{} + want int64 + }{ + { + failure: false, + name: "postgres without filters", + database: _postgres, + filters: map[string]interface{}{}, + want: 2, + }, + { + failure: false, + name: "postgres with event filter", + database: _postgres, + filters: map[string]interface{}{ + "event": "push", + }, + want: 2, + }, + { + failure: false, + name: "sqlite3 without filters", + database: _sqlite, + filters: map[string]interface{}{}, + want: 2, + }, + { + failure: false, + name: "sqlite3 with event filter", + database: _sqlite, + filters: map[string]interface{}{ + "event": "push", + }, + want: 2, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, err := test.database.CountBuildsForOrg("foo", test.filters) + + if test.failure { + if err == nil { + t.Errorf("CountBuildsForOrg for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("CountBuildsForOrg for %s returned err: %v", test.name, err) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("CountBuildsForOrg for %s is %v, want %v", test.name, got, test.want) + } + }) + } +} diff --git a/database/build/count_repo.go b/database/build/count_repo.go new file mode 100644 index 000000000..b49a415e0 --- /dev/null +++ b/database/build/count_repo.go @@ -0,0 +1,32 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "github.com/go-vela/types/constants" + "github.com/go-vela/types/library" + "github.com/sirupsen/logrus" +) + +// CountBuildsForRepo gets the count of builds by repo ID from the database. +func (e *engine) CountBuildsForRepo(r *library.Repo, filters map[string]interface{}) (int64, error) { + e.logger.WithFields(logrus.Fields{ + "org": r.GetOrg(), + "repo": r.GetName(), + }).Tracef("getting count of builds for repo %s from the database", r.GetFullName()) + + // variable to store query results + var b int64 + + // send query to the database and store result in variable + err := e.client. + Table(constants.TableBuild). + Where("repo_id = ?", r.GetID()). + Where(filters). + Count(&b). + Error + + return b, err +} diff --git a/database/build/count_repo_test.go b/database/build/count_repo_test.go new file mode 100644 index 000000000..673887183 --- /dev/null +++ b/database/build/count_repo_test.go @@ -0,0 +1,104 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "reflect" + "testing" + + "github.com/DATA-DOG/go-sqlmock" +) + +func TestBuild_Engine_CountBuildsForRepo(t *testing.T) { + // setup types + _buildOne := testBuild() + _buildOne.SetID(1) + _buildOne.SetRepoID(1) + _buildOne.SetNumber(1) + _buildOne.SetDeployPayload(nil) + + _buildTwo := testBuild() + _buildTwo.SetID(2) + _buildTwo.SetRepoID(1) + _buildTwo.SetNumber(2) + _buildTwo.SetDeployPayload(nil) + + _repo := testRepo() + _repo.SetID(1) + _repo.SetUserID(1) + _repo.SetHash("baz") + _repo.SetOrg("foo") + _repo.SetName("bar") + _repo.SetFullName("foo/bar") + _repo.SetVisibility("public") + + _postgres, _mock := testPostgres(t) + defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() + + // create expected result in mock + _rows := sqlmock.NewRows([]string{"count"}).AddRow(2) + + // ensure the mock expects the query + _mock.ExpectQuery(`SELECT count(*) FROM "builds" WHERE repo_id = $1`).WithArgs(1).WillReturnRows(_rows) + + _sqlite := testSqlite(t) + defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() + + err := _sqlite.CreateBuild(_buildOne) + if err != nil { + t.Errorf("unable to create test build for sqlite: %v", err) + } + + err = _sqlite.CreateBuild(_buildTwo) + if err != nil { + t.Errorf("unable to create test build for sqlite: %v", err) + } + + // setup tests + tests := []struct { + failure bool + name string + database *engine + want int64 + }{ + { + failure: false, + name: "postgres", + database: _postgres, + want: 2, + }, + { + failure: false, + name: "sqlite3", + database: _sqlite, + want: 2, + }, + } + + filters := map[string]interface{}{} + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, err := test.database.CountBuildsForRepo(_repo, filters) + + if test.failure { + if err == nil { + t.Errorf("CountBuildsForRepo for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("CountBuildsForRepo for %s returned err: %v", test.name, err) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("CountBuildsForRepo for %s is %v, want %v", test.name, got, test.want) + } + }) + } +} diff --git a/database/build/count_status.go b/database/build/count_status.go new file mode 100644 index 000000000..af6a9f957 --- /dev/null +++ b/database/build/count_status.go @@ -0,0 +1,27 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "github.com/go-vela/types/constants" +) + +// CountBuildsForStatus gets the count of builds by status from the database. +func (e *engine) CountBuildsForStatus(status string, filters map[string]interface{}) (int64, error) { + e.logger.Tracef("getting count of builds for status %s from the database", status) + + // variable to store query results + var b int64 + + // send query to the database and store result in variable + err := e.client. + Table(constants.TableBuild). + Where("status = ?", status). + Where(filters). + Count(&b). + Error + + return b, err +} diff --git a/database/build/count_status_test.go b/database/build/count_status_test.go new file mode 100644 index 000000000..f88759d1c --- /dev/null +++ b/database/build/count_status_test.go @@ -0,0 +1,97 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "reflect" + "testing" + + "github.com/DATA-DOG/go-sqlmock" +) + +func TestBuild_Engine_CountBuildsForStatus(t *testing.T) { + // setup types + _buildOne := testBuild() + _buildOne.SetID(1) + _buildOne.SetRepoID(1) + _buildOne.SetNumber(1) + _buildOne.SetDeployPayload(nil) + _buildOne.SetStatus("running") + + _buildTwo := testBuild() + _buildTwo.SetID(2) + _buildTwo.SetRepoID(1) + _buildTwo.SetNumber(2) + _buildTwo.SetDeployPayload(nil) + _buildTwo.SetStatus("running") + + _postgres, _mock := testPostgres(t) + defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() + + // create expected result in mock + _rows := sqlmock.NewRows([]string{"count"}).AddRow(2) + + // ensure the mock expects the query + _mock.ExpectQuery(`SELECT count(*) FROM "builds" WHERE status = $1`).WithArgs("running").WillReturnRows(_rows) + + _sqlite := testSqlite(t) + defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() + + err := _sqlite.CreateBuild(_buildOne) + if err != nil { + t.Errorf("unable to create test build for sqlite: %v", err) + } + + err = _sqlite.CreateBuild(_buildTwo) + if err != nil { + t.Errorf("unable to create test build for sqlite: %v", err) + } + + // setup tests + tests := []struct { + failure bool + name string + database *engine + want int64 + }{ + { + failure: false, + name: "postgres", + database: _postgres, + want: 2, + }, + { + failure: false, + name: "sqlite3", + database: _sqlite, + want: 2, + }, + } + + filters := map[string]interface{}{} + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, err := test.database.CountBuildsForStatus("running", filters) + + if test.failure { + if err == nil { + t.Errorf("CountBuildsForStatus for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("CountBuildsForStatus for %s returned err: %v", test.name, err) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("CountBuildsForStatus for %s is %v, want %v", test.name, got, test.want) + } + }) + } +} diff --git a/database/build/count_test.go b/database/build/count_test.go new file mode 100644 index 000000000..6254648ee --- /dev/null +++ b/database/build/count_test.go @@ -0,0 +1,93 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "reflect" + "testing" + + "github.com/DATA-DOG/go-sqlmock" +) + +func TestBuild_Engine_CountBuilds(t *testing.T) { + // setup types + _buildOne := testBuild() + _buildOne.SetID(1) + _buildOne.SetRepoID(1) + _buildOne.SetNumber(1) + _buildOne.SetDeployPayload(nil) + + _buildTwo := testBuild() + _buildTwo.SetID(2) + _buildTwo.SetRepoID(1) + _buildTwo.SetNumber(2) + _buildTwo.SetDeployPayload(nil) + + _postgres, _mock := testPostgres(t) + defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() + + // create expected result in mock + _rows := sqlmock.NewRows([]string{"count"}).AddRow(2) + + // ensure the mock expects the query + _mock.ExpectQuery(`SELECT count(*) FROM "builds"`).WillReturnRows(_rows) + + _sqlite := testSqlite(t) + defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() + + err := _sqlite.CreateBuild(_buildOne) + if err != nil { + t.Errorf("unable to create test build for sqlite: %v", err) + } + + err = _sqlite.CreateBuild(_buildTwo) + if err != nil { + t.Errorf("unable to create test build for sqlite: %v", err) + } + + // setup tests + tests := []struct { + failure bool + name string + database *engine + want int64 + }{ + { + failure: false, + name: "postgres", + database: _postgres, + want: 2, + }, + { + failure: false, + name: "sqlite3", + database: _sqlite, + want: 2, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, err := test.database.CountBuilds() + + if test.failure { + if err == nil { + t.Errorf("CountBuilds for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("CountBuilds for %s returned err: %v", test.name, err) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("CountBuilds for %s is %v, want %v", test.name, got, test.want) + } + }) + } +} diff --git a/database/build/create.go b/database/build/create.go new file mode 100644 index 000000000..911c6f148 --- /dev/null +++ b/database/build/create.go @@ -0,0 +1,39 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +//nolint:dupl // ignore similar code with update.go +package build + +import ( + "github.com/go-vela/types/constants" + "github.com/go-vela/types/database" + "github.com/go-vela/types/library" + "github.com/sirupsen/logrus" +) + +// CreateBuild creates a new build in the database. +func (e *engine) CreateBuild(b *library.Build) error { + e.logger.WithFields(logrus.Fields{ + "build": b.GetNumber(), + }).Tracef("creating build %d in the database", b.GetNumber()) + + // cast the library type to database type + // + // https://pkg.go.dev/github.com/go-vela/types/database#BuildFromLibrary + build := database.BuildFromLibrary(b) + + // validate the necessary fields are populated + // + // https://pkg.go.dev/github.com/go-vela/types/database#Build.Validate + err := build.Validate() + if err != nil { + return err + } + + // send query to the database + return e.client. + Table(constants.TableBuild). + Create(build.Crop()). + Error +} diff --git a/database/build/create_test.go b/database/build/create_test.go new file mode 100644 index 000000000..3cfe1fdf9 --- /dev/null +++ b/database/build/create_test.go @@ -0,0 +1,73 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "testing" + + "github.com/DATA-DOG/go-sqlmock" +) + +func TestBuild_Engine_CreateBuild(t *testing.T) { + // setup types + _build := testBuild() + _build.SetID(1) + _build.SetRepoID(1) + _build.SetNumber(1) + _build.SetDeployPayload(nil) + + _postgres, _mock := testPostgres(t) + defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() + + // create expected result in mock + _rows := sqlmock.NewRows([]string{"id"}).AddRow(1) + + // ensure the mock expects the query + _mock.ExpectQuery(`INSERT INTO "builds" +("repo_id","pipeline_id","number","parent","event","event_action","status","error","enqueued","created","started","finished","deploy","deploy_payload","clone","source","title","message","commit","sender","author","email","link","branch","ref","base_ref","head_ref","host","runtime","distribution","id") +VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$30,$31) RETURNING "id"`). + WithArgs(1, nil, 1, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, AnyArgument{}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 1). + WillReturnRows(_rows) + + _sqlite := testSqlite(t) + defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() + + // setup tests + tests := []struct { + failure bool + name string + database *engine + }{ + { + failure: false, + name: "postgres", + database: _postgres, + }, + { + failure: false, + name: "sqlite3", + database: _sqlite, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + err := test.database.CreateBuild(_build) + + if test.failure { + if err == nil { + t.Errorf("CreateBuild for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("CreateBuild for %s returned err: %v", test.name, err) + } + }) + } +} diff --git a/database/build/delete.go b/database/build/delete.go new file mode 100644 index 000000000..76643eed1 --- /dev/null +++ b/database/build/delete.go @@ -0,0 +1,30 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "github.com/go-vela/types/constants" + "github.com/go-vela/types/database" + "github.com/go-vela/types/library" + "github.com/sirupsen/logrus" +) + +// DeleteBuild deletes an existing build from the database. +func (e *engine) DeleteBuild(b *library.Build) error { + e.logger.WithFields(logrus.Fields{ + "build": b.GetNumber(), + }).Tracef("deleting build %d from the database", b.GetNumber()) + + // cast the library type to database type + // + // https://pkg.go.dev/github.com/go-vela/types/database#BuildFromLibrary + build := database.BuildFromLibrary(b) + + // send query to the database + return e.client. + Table(constants.TableBuild). + Delete(build). + Error +} diff --git a/database/build/delete_test.go b/database/build/delete_test.go new file mode 100644 index 000000000..91dd1c9c7 --- /dev/null +++ b/database/build/delete_test.go @@ -0,0 +1,73 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "testing" + + "github.com/DATA-DOG/go-sqlmock" +) + +func TestBuild_Engine_DeleteBuild(t *testing.T) { + // setup types + _build := testBuild() + _build.SetID(1) + _build.SetRepoID(1) + _build.SetNumber(1) + _build.SetDeployPayload(nil) + + _postgres, _mock := testPostgres(t) + defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() + + // ensure the mock expects the query + _mock.ExpectExec(`DELETE FROM "builds" WHERE "builds"."id" = $1`). + WithArgs(1). + WillReturnResult(sqlmock.NewResult(1, 1)) + + _sqlite := testSqlite(t) + defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() + + err := _sqlite.CreateBuild(_build) + if err != nil { + t.Errorf("unable to create test build for sqlite: %v", err) + } + + // setup tests + tests := []struct { + failure bool + name string + database *engine + }{ + { + failure: false, + name: "postgres", + database: _postgres, + }, + { + failure: false, + name: "sqlite3", + database: _sqlite, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + err = test.database.DeleteBuild(_build) + + if test.failure { + if err == nil { + t.Errorf("DeleteBuild for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("DeleteBuild for %s returned err: %v", test.name, err) + } + }) + } +} diff --git a/database/build/get.go b/database/build/get.go new file mode 100644 index 000000000..236d62cca --- /dev/null +++ b/database/build/get.go @@ -0,0 +1,31 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "github.com/go-vela/types/constants" + "github.com/go-vela/types/database" + "github.com/go-vela/types/library" +) + +// GetBuild gets a build by ID from the database. +func (e *engine) GetBuild(id int64) (*library.Build, error) { + e.logger.Tracef("getting build %d from the database", id) + + // variable to store query results + b := new(database.Build) + + // send query to the database and store result in variable + err := e.client. + Table(constants.TableBuild). + Where("id = ?", id). + Take(b). + Error + if err != nil { + return nil, err + } + + return b.ToLibrary(), nil +} diff --git a/database/build/get_repo.go b/database/build/get_repo.go new file mode 100644 index 000000000..8d93dcb3c --- /dev/null +++ b/database/build/get_repo.go @@ -0,0 +1,37 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "github.com/go-vela/types/constants" + "github.com/go-vela/types/database" + "github.com/go-vela/types/library" + "github.com/sirupsen/logrus" +) + +// GetBuildForRepo gets a build by repo ID and number from the database. +func (e *engine) GetBuildForRepo(r *library.Repo, number int) (*library.Build, error) { + e.logger.WithFields(logrus.Fields{ + "build": number, + "org": r.GetOrg(), + "repo": r.GetName(), + }).Tracef("getting build %s/%d from the database", r.GetFullName(), number) + + // variable to store query results + b := new(database.Build) + + // send query to the database and store result in variable + err := e.client. + Table(constants.TableBuild). + Where("repo_id = ?", r.GetID()). + Where("number = ?", number). + Take(b). + Error + if err != nil { + return nil, err + } + + return b.ToLibrary(), nil +} diff --git a/database/build/get_repo_test.go b/database/build/get_repo_test.go new file mode 100644 index 000000000..f1341d960 --- /dev/null +++ b/database/build/get_repo_test.go @@ -0,0 +1,94 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "reflect" + "testing" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/go-vela/types/library" +) + +func TestBuild_Engine_GetBuildForRepo(t *testing.T) { + // setup types + _build := testBuild() + _build.SetID(1) + _build.SetRepoID(1) + _build.SetNumber(1) + _build.SetDeployPayload(nil) + + _repo := testRepo() + _repo.SetID(1) + _repo.SetUserID(1) + _repo.SetHash("baz") + _repo.SetOrg("foo") + _repo.SetName("bar") + _repo.SetFullName("foo/bar") + _repo.SetVisibility("public") + + _postgres, _mock := testPostgres(t) + defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() + + // create expected result in mock + _rows := sqlmock.NewRows( + []string{"id", "repo_id", "pipeline_id", "number", "parent", "event", "event_action", "status", "error", "enqueued", "created", "started", "finished", "deploy", "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, "", nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0) + + // ensure the mock expects the query + _mock.ExpectQuery(`SELECT * FROM "builds" WHERE repo_id = $1 AND number = $2 LIMIT 1`).WithArgs(1, 1).WillReturnRows(_rows) + + _sqlite := testSqlite(t) + defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() + + err := _sqlite.CreateBuild(_build) + if err != nil { + t.Errorf("unable to create test build for sqlite: %v", err) + } + + // setup tests + tests := []struct { + failure bool + name string + database *engine + want *library.Build + }{ + { + failure: false, + name: "postgres", + database: _postgres, + want: _build, + }, + { + failure: false, + name: "sqlite3", + database: _sqlite, + want: _build, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, err := test.database.GetBuildForRepo(_repo, 1) + + if test.failure { + if err == nil { + t.Errorf("GetBuildForRepo for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("GetBuildForRepo for %s returned err: %v", test.name, err) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("GetBuildForRepo for %s is %v, want %v", test.name, got, test.want) + } + }) + } +} diff --git a/database/build/get_test.go b/database/build/get_test.go new file mode 100644 index 000000000..7aec15f13 --- /dev/null +++ b/database/build/get_test.go @@ -0,0 +1,85 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "reflect" + "testing" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/go-vela/types/library" +) + +func TestBuild_Engine_GetBuild(t *testing.T) { + // setup types + _build := testBuild() + _build.SetID(1) + _build.SetRepoID(1) + _build.SetNumber(1) + _build.SetDeployPayload(nil) + + _postgres, _mock := testPostgres(t) + defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() + + // create expected result in mock + _rows := sqlmock.NewRows( + []string{"id", "repo_id", "pipeline_id", "number", "parent", "event", "event_action", "status", "error", "enqueued", "created", "started", "finished", "deploy", "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, "", nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0) + + // ensure the mock expects the query + _mock.ExpectQuery(`SELECT * FROM "builds" WHERE id = $1 LIMIT 1`).WithArgs(1).WillReturnRows(_rows) + + _sqlite := testSqlite(t) + defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() + + err := _sqlite.CreateBuild(_build) + if err != nil { + t.Errorf("unable to create test build for sqlite: %v", err) + } + + // setup tests + tests := []struct { + failure bool + name string + database *engine + want *library.Build + }{ + { + failure: false, + name: "postgres", + database: _postgres, + want: _build, + }, + { + failure: false, + name: "sqlite3", + database: _sqlite, + want: _build, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, err := test.database.GetBuild(1) + + if test.failure { + if err == nil { + t.Errorf("GetBuild for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("GetBuild for %s returned err: %v", test.name, err) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("GetBuild for %s is %v, want %v", test.name, got, test.want) + } + }) + } +} diff --git a/database/build/index.go b/database/build/index.go new file mode 100644 index 000000000..77e15e451 --- /dev/null +++ b/database/build/index.go @@ -0,0 +1,69 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +const ( + // CreateCreatedIndex represents a query to create an + // index on the builds table for the created column. + CreateCreatedIndex = ` +CREATE INDEX +IF NOT EXISTS +builds_created +ON builds (created); +` + + // CreateRepoIDIndex represents a query to create an + // index on the builds table for the repo_id column. + CreateRepoIDIndex = ` +CREATE INDEX +IF NOT EXISTS +builds_repo_id +ON builds (repo_id); +` + + // CreateSourceIndex represents a query to create an + // index on the builds table for the source column. + CreateSourceIndex = ` +CREATE INDEX +IF NOT EXISTS +builds_source +ON builds (source); +` + + // CreateStatusIndex represents a query to create an + // index on the builds table for the status column. + CreateStatusIndex = ` +CREATE INDEX +IF NOT EXISTS +builds_status +ON builds (status); +` +) + +// CreateBuildIndexes creates the indexes for the builds table in the database. +func (e *engine) CreateBuildIndexes() error { + e.logger.Tracef("creating indexes for builds table in the database") + + // create the created column index for the builds table + err := e.client.Exec(CreateCreatedIndex).Error + if err != nil { + return err + } + + // create the repo_id column index for the builds table + err = e.client.Exec(CreateRepoIDIndex).Error + if err != nil { + return err + } + + // create the source column index for the builds table + err = e.client.Exec(CreateSourceIndex).Error + if err != nil { + return err + } + + // create the status column index for the builds table + return e.client.Exec(CreateStatusIndex).Error +} diff --git a/database/build/index_test.go b/database/build/index_test.go new file mode 100644 index 000000000..bd61ff5d2 --- /dev/null +++ b/database/build/index_test.go @@ -0,0 +1,62 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "testing" + + "github.com/DATA-DOG/go-sqlmock" +) + +func TestBuild_Engine_CreateBuildIndexes(t *testing.T) { + // setup types + _postgres, _mock := testPostgres(t) + defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() + + _mock.ExpectExec(CreateCreatedIndex).WillReturnResult(sqlmock.NewResult(1, 1)) + _mock.ExpectExec(CreateRepoIDIndex).WillReturnResult(sqlmock.NewResult(1, 1)) + _mock.ExpectExec(CreateSourceIndex).WillReturnResult(sqlmock.NewResult(1, 1)) + _mock.ExpectExec(CreateStatusIndex).WillReturnResult(sqlmock.NewResult(1, 1)) + + _sqlite := testSqlite(t) + defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() + + // setup tests + tests := []struct { + failure bool + name string + database *engine + }{ + { + failure: false, + name: "postgres", + database: _postgres, + }, + { + failure: false, + name: "sqlite3", + database: _sqlite, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + err := test.database.CreateBuildIndexes() + + if test.failure { + if err == nil { + t.Errorf("CreateBuildIndexes for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("CreateBuildIndexes for %s returned err: %v", test.name, err) + } + }) + } +} diff --git a/database/build/interface.go b/database/build/interface.go new file mode 100644 index 000000000..e4264f6a8 --- /dev/null +++ b/database/build/interface.go @@ -0,0 +1,61 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "github.com/go-vela/types/library" +) + +// BuildInterface represents the Vela interface for build +// functions with the supported Database backends. +// +//nolint:revive // ignore name stutter +type BuildInterface interface { + // Build Data Definition Language Functions + // + // https://en.wikipedia.org/wiki/Data_definition_language + + // CreateBuildIndexes defines a function that creates the indexes for the builds table. + CreateBuildIndexes() error + // CreateBuildTable defines a function that creates the builds table. + CreateBuildTable(string) error + + // Build Data Manipulation Language Functions + // + // https://en.wikipedia.org/wiki/Data_manipulation_language + + // CountBuilds defines a function that gets the count of all builds. + CountBuilds() (int64, error) + // CountBuildsForDeployment defines a function that gets the count of builds by deployment url. + CountBuildsForDeployment(*library.Deployment, map[string]interface{}) (int64, error) + // CountBuildsForOrg defines a function that gets the count of builds by org name. + CountBuildsForOrg(string, map[string]interface{}) (int64, error) + // CountBuildsForRepo defines a function that gets the count of builds by repo ID. + CountBuildsForRepo(*library.Repo, map[string]interface{}) (int64, error) + // CountBuildsForStatus defines a function that gets the count of builds by status. + CountBuildsForStatus(string, map[string]interface{}) (int64, error) + // CreateBuild defines a function that creates a new build. + CreateBuild(*library.Build) error + // DeleteBuild defines a function that deletes an existing build. + DeleteBuild(*library.Build) error + // GetBuild defines a function that gets a build by ID. + GetBuild(int64) (*library.Build, error) + // GetBuildForRepo defines a function that gets a build by repo ID and number. + GetBuildForRepo(*library.Repo, int) (*library.Build, error) + // LastBuildForRepo defines a function that gets the last build ran by repo ID and branch. + LastBuildForRepo(*library.Repo, string) (*library.Build, error) + // ListBuilds defines a function that gets a list of all builds. + ListBuilds() ([]*library.Build, error) + // ListBuildsForDeployment defines a function that gets a list of builds by deployment url. + ListBuildsForDeployment(*library.Deployment, map[string]interface{}, int, int) ([]*library.Build, int64, error) + // ListBuildsForOrg defines a function that gets a list of builds by org name. + ListBuildsForOrg(string, map[string]interface{}, int, int) ([]*library.Build, int64, error) + // ListBuildsForRepo defines a function that gets a list of builds by repo ID. + ListBuildsForRepo(*library.Repo, map[string]interface{}, int64, int64, int, int) ([]*library.Build, int64, error) + // ListPendingAndRunningBuilds defines a function that gets a list of pending and running builds. + ListPendingAndRunningBuilds(string) ([]*library.BuildQueue, error) + // UpdateBuild defines a function that updates an existing build. + UpdateBuild(*library.Build) error +} diff --git a/database/build/last_repo.go b/database/build/last_repo.go new file mode 100644 index 000000000..ed0adebbf --- /dev/null +++ b/database/build/last_repo.go @@ -0,0 +1,47 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "errors" + + "github.com/go-vela/types/constants" + "github.com/go-vela/types/database" + "github.com/go-vela/types/library" + "github.com/sirupsen/logrus" + + "gorm.io/gorm" +) + +// LastBuildForRepo gets the last build by repo ID and branch from the database. +func (e *engine) LastBuildForRepo(r *library.Repo, branch string) (*library.Build, error) { + e.logger.WithFields(logrus.Fields{ + "org": r.GetOrg(), + "repo": r.GetName(), + }).Tracef("getting last build for repo %s from the database", r.GetFullName()) + + // variable to store query results + b := new(database.Build) + + // send query to the database and store result in variable + err := e.client. + Table(constants.TableBuild). + Where("repo_id = ?", r.GetID()). + Where("branch = ?", branch). + Order("number DESC"). + Take(b). + Error + if err != nil { + // check if the query returned a record not found error + if errors.Is(err, gorm.ErrRecordNotFound) { + // the record will not exist if it is a new repo + return nil, nil + } + + return nil, err + } + + return b.ToLibrary(), nil +} diff --git a/database/build/last_repo_test.go b/database/build/last_repo_test.go new file mode 100644 index 000000000..48cb32c01 --- /dev/null +++ b/database/build/last_repo_test.go @@ -0,0 +1,95 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "reflect" + "testing" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/go-vela/types/library" +) + +func TestBuild_Engine_LastBuildForRepo(t *testing.T) { + // setup types + _build := testBuild() + _build.SetID(1) + _build.SetRepoID(1) + _build.SetNumber(1) + _build.SetDeployPayload(nil) + _build.SetBranch("master") + + _repo := testRepo() + _repo.SetID(1) + _repo.SetUserID(1) + _repo.SetHash("baz") + _repo.SetOrg("foo") + _repo.SetName("bar") + _repo.SetFullName("foo/bar") + _repo.SetVisibility("public") + + _postgres, _mock := testPostgres(t) + defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() + + // create expected result in mock + _rows := sqlmock.NewRows( + []string{"id", "repo_id", "pipeline_id", "number", "parent", "event", "event_action", "status", "error", "enqueued", "created", "started", "finished", "deploy", "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, "", nil, "", "", "", "", "", "", "", "", "", "master", "", "", "", "", "", "", 0) + + // ensure the mock expects the query + _mock.ExpectQuery(`SELECT * FROM "builds" WHERE repo_id = $1 AND branch = $2 ORDER BY number DESC LIMIT 1`).WithArgs(1, "master").WillReturnRows(_rows) + + _sqlite := testSqlite(t) + defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() + + err := _sqlite.CreateBuild(_build) + if err != nil { + t.Errorf("unable to create test build for sqlite: %v", err) + } + + // setup tests + tests := []struct { + failure bool + name string + database *engine + want *library.Build + }{ + { + failure: false, + name: "postgres", + database: _postgres, + want: _build, + }, + { + failure: false, + name: "sqlite3", + database: _sqlite, + want: _build, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, err := test.database.LastBuildForRepo(_repo, "master") + + if test.failure { + if err == nil { + t.Errorf("LastBuildForRepo for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("LastBuildForRepo for %s returned err: %v", test.name, err) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("LastBuildForRepo for %s is %v, want %v", test.name, got, test.want) + } + }) + } +} diff --git a/database/build/list.go b/database/build/list.go new file mode 100644 index 000000000..1139cf490 --- /dev/null +++ b/database/build/list.go @@ -0,0 +1,54 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "github.com/go-vela/types/constants" + "github.com/go-vela/types/database" + "github.com/go-vela/types/library" +) + +// ListBuilds gets a list of all builds from the database. +func (e *engine) ListBuilds() ([]*library.Build, error) { + e.logger.Trace("listing all builds from the database") + + // variables to store query results and return value + count := int64(0) + b := new([]database.Build) + builds := []*library.Build{} + + // count the results + count, err := e.CountBuilds() + if err != nil { + return nil, err + } + + // short-circuit if there are no results + if count == 0 { + return builds, nil + } + + // send query to the database and store result in variable + err = e.client. + Table(constants.TableBuild). + Find(&b). + Error + if err != nil { + return nil, err + } + + // iterate through all query results + for _, build := range *b { + // https://golang.org/doc/faq#closures_and_goroutines + tmp := build + + // convert query result to library type + // + // https://pkg.go.dev/github.com/go-vela/types/database#Build.ToLibrary + builds = append(builds, tmp.ToLibrary()) + } + + return builds, nil +} diff --git a/database/build/list_deployment.go b/database/build/list_deployment.go new file mode 100644 index 000000000..ff397f2ae --- /dev/null +++ b/database/build/list_deployment.go @@ -0,0 +1,66 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "github.com/go-vela/types/constants" + "github.com/go-vela/types/database" + "github.com/go-vela/types/library" + "github.com/sirupsen/logrus" +) + +// ListBuildsForDeployment gets a list of builds by deployment url from the database. +// +//nolint:lll // ignore long line length due to variable names +func (e *engine) ListBuildsForDeployment(d *library.Deployment, filters map[string]interface{}, page, perPage int) ([]*library.Build, int64, error) { + e.logger.WithFields(logrus.Fields{ + "deployment": d.GetURL(), + }).Tracef("listing builds for deployment %s from the database", d.GetURL()) + + // variables to store query results and return values + count := int64(0) + b := new([]database.Build) + builds := []*library.Build{} + + // count the results + count, err := e.CountBuildsForDeployment(d, filters) + if err != nil { + return builds, 0, err + } + + // short-circuit if there are no results + if count == 0 { + return builds, 0, nil + } + + // calculate offset for pagination through results + offset := perPage * (page - 1) + + err = e.client. + Table(constants.TableBuild). + Where("source = ?", d.GetURL()). + Where(filters). + Order("number DESC"). + Limit(perPage). + Offset(offset). + Find(&b). + Error + if err != nil { + return nil, count, err + } + + // iterate through all query results + for _, build := range *b { + // https://golang.org/doc/faq#closures_and_goroutines + tmp := build + + // convert query result to library type + // + // https://pkg.go.dev/github.com/go-vela/types/database#Build.ToLibrary + builds = append(builds, tmp.ToLibrary()) + } + + return builds, count, nil +} diff --git a/database/build/list_deployment_test.go b/database/build/list_deployment_test.go new file mode 100644 index 000000000..94983c56e --- /dev/null +++ b/database/build/list_deployment_test.go @@ -0,0 +1,112 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "reflect" + "testing" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/go-vela/types/library" +) + +func TestBuild_Engine_ListBuildsForDeployment(t *testing.T) { + // setup types + _buildOne := testBuild() + _buildOne.SetID(1) + _buildOne.SetRepoID(1) + _buildOne.SetNumber(1) + _buildOne.SetDeployPayload(nil) + _buildOne.SetSource("https://github.com/github/octocat/deployments/1") + + _buildTwo := testBuild() + _buildTwo.SetID(2) + _buildTwo.SetRepoID(1) + _buildTwo.SetNumber(2) + _buildTwo.SetDeployPayload(nil) + _buildTwo.SetSource("https://github.com/github/octocat/deployments/1") + + _deployment := testDeployment() + _deployment.SetID(1) + _deployment.SetRepoID(1) + _deployment.SetURL("https://github.com/github/octocat/deployments/1") + + _postgres, _mock := testPostgres(t) + defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() + + // create expected count query result in mock + _rows := sqlmock.NewRows([]string{"count"}).AddRow(2) + + // ensure the mock expects the count query + _mock.ExpectQuery(`SELECT count(*) FROM "builds" WHERE source = $1`).WithArgs("https://github.com/github/octocat/deployments/1").WillReturnRows(_rows) + + // create expected query result in mock + _rows = sqlmock.NewRows( + []string{"id", "repo_id", "pipeline_id", "number", "parent", "event", "event_action", "status", "error", "enqueued", "created", "started", "finished", "deploy", "deploy_payload", "clone", "source", "title", "message", "commit", "sender", "author", "email", "link", "branch", "ref", "base_ref", "head_ref", "host", "runtime", "distribution", "timestamp"}). + AddRow(2, 1, nil, 2, 0, "", "", "", "", 0, 0, 0, 0, "", nil, "", "https://github.com/github/octocat/deployments/1", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0). + AddRow(1, 1, nil, 1, 0, "", "", "", "", 0, 0, 0, 0, "", nil, "", "https://github.com/github/octocat/deployments/1", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0) + + // ensure the mock expects the query + _mock.ExpectQuery(`SELECT * FROM "builds" WHERE source = $1 ORDER BY number DESC LIMIT 10`).WithArgs("https://github.com/github/octocat/deployments/1").WillReturnRows(_rows) + + _sqlite := testSqlite(t) + defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() + + err := _sqlite.CreateBuild(_buildOne) + if err != nil { + t.Errorf("unable to create test build for sqlite: %v", err) + } + + err = _sqlite.CreateBuild(_buildTwo) + if err != nil { + t.Errorf("unable to create test build for sqlite: %v", err) + } + + // setup tests + tests := []struct { + failure bool + name string + database *engine + want []*library.Build + }{ + { + failure: false, + name: "postgres", + database: _postgres, + want: []*library.Build{_buildTwo, _buildOne}, + }, + { + failure: false, + name: "sqlite3", + database: _sqlite, + want: []*library.Build{_buildTwo, _buildOne}, + }, + } + + filters := map[string]interface{}{} + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, _, err := test.database.ListBuildsForDeployment(_deployment, filters, 1, 10) + + if test.failure { + if err == nil { + t.Errorf("ListBuildsForDeployment for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("ListBuildsForDeployment for %s returned err: %v", test.name, err) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("ListBuildsForDeployment for %s is %v, want %v", test.name, got, test.want) + } + }) + } +} diff --git a/database/build/list_org.go b/database/build/list_org.go new file mode 100644 index 000000000..8f4e62e58 --- /dev/null +++ b/database/build/list_org.go @@ -0,0 +1,69 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "github.com/go-vela/types/constants" + "github.com/go-vela/types/database" + "github.com/go-vela/types/library" + "github.com/sirupsen/logrus" +) + +// ListBuildsForOrg gets a list of builds by org name from the database. +// +//nolint:lll // ignore long line length due to variable names +func (e *engine) ListBuildsForOrg(org string, filters map[string]interface{}, page, perPage int) ([]*library.Build, int64, error) { + e.logger.WithFields(logrus.Fields{ + "org": org, + }).Tracef("listing builds for org %s from the database", org) + + // variables to store query results and return values + count := int64(0) + b := new([]database.Build) + builds := []*library.Build{} + + // count the results + count, err := e.CountBuildsForOrg(org, filters) + if err != nil { + return builds, 0, err + } + + // short-circuit if there are no results + if count == 0 { + return builds, 0, nil + } + + // calculate offset for pagination through results + offset := perPage * (page - 1) + + err = e.client. + Table(constants.TableBuild). + Select("builds.*"). + Joins("JOIN repos ON builds.repo_id = repos.id"). + Where("repos.org = ?", org). + Where(filters). + Order("created DESC"). + Order("id"). + Limit(perPage). + Offset(offset). + Find(&b). + Error + if err != nil { + return nil, count, err + } + + // iterate through all query results + for _, build := range *b { + // https://golang.org/doc/faq#closures_and_goroutines + tmp := build + + // convert query result to library type + // + // https://pkg.go.dev/github.com/go-vela/types/database#Build.ToLibrary + builds = append(builds, tmp.ToLibrary()) + } + + return builds, count, nil +} diff --git a/database/build/list_org_test.go b/database/build/list_org_test.go new file mode 100644 index 000000000..e9121e024 --- /dev/null +++ b/database/build/list_org_test.go @@ -0,0 +1,204 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "reflect" + "testing" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/go-vela/types/constants" + "github.com/go-vela/types/database" + "github.com/go-vela/types/library" +) + +func TestBuild_Engine_ListBuildsForOrg(t *testing.T) { + // setup types + _buildOne := testBuild() + _buildOne.SetID(1) + _buildOne.SetRepoID(1) + _buildOne.SetNumber(1) + _buildOne.SetDeployPayload(nil) + _buildOne.SetEvent("push") + + _buildTwo := testBuild() + _buildTwo.SetID(2) + _buildTwo.SetRepoID(2) + _buildTwo.SetNumber(2) + _buildTwo.SetDeployPayload(nil) + _buildTwo.SetEvent("push") + + _repoOne := testRepo() + _repoOne.SetID(1) + _repoOne.SetUserID(1) + _repoOne.SetHash("baz") + _repoOne.SetOrg("foo") + _repoOne.SetName("bar") + _repoOne.SetFullName("foo/bar") + _repoOne.SetVisibility("public") + _repoOne.SetPipelineType("yaml") + _repoOne.SetTopics([]string{}) + + _repoTwo := testRepo() + _repoTwo.SetID(2) + _repoTwo.SetUserID(1) + _repoTwo.SetHash("bar") + _repoTwo.SetOrg("foo") + _repoTwo.SetName("baz") + _repoTwo.SetFullName("foo/baz") + _repoTwo.SetVisibility("public") + _repoTwo.SetPipelineType("yaml") + _repoTwo.SetTopics([]string{}) + + _postgres, _mock := testPostgres(t) + defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() + + // create expected count query without filters result in mock + _rows := sqlmock.NewRows([]string{"count"}).AddRow(2) + // ensure the mock expects the count query without filters + _mock.ExpectQuery(`SELECT count(*) FROM "builds" JOIN repos ON builds.repo_id = repos.id WHERE repos.org = $1`).WithArgs("foo").WillReturnRows(_rows) + // create expected query without filters result in mock + _rows = sqlmock.NewRows( + []string{"id", "repo_id", "pipeline_id", "number", "parent", "event", "event_action", "status", "error", "enqueued", "created", "started", "finished", "deploy", "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, "push", "", "", "", 0, 0, 0, 0, "", nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0). + AddRow(2, 2, nil, 2, 0, "push", "", "", "", 0, 0, 0, 0, "", nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0) + // ensure the mock expects the query without filters + _mock.ExpectQuery(`SELECT builds.* FROM "builds" JOIN repos ON builds.repo_id = repos.id WHERE repos.org = $1 ORDER BY created DESC,id LIMIT 10`).WithArgs("foo").WillReturnRows(_rows) + + // create expected count query with event filter result in mock + _rows = sqlmock.NewRows([]string{"count"}).AddRow(2) + // ensure the mock expects the count query with event filter + _mock.ExpectQuery(`SELECT count(*) FROM "builds" JOIN repos ON builds.repo_id = repos.id WHERE repos.org = $1 AND "event" = $2`).WithArgs("foo", "push").WillReturnRows(_rows) + // create expected query with event filter result in mock + _rows = sqlmock.NewRows( + []string{"id", "repo_id", "pipeline_id", "number", "parent", "event", "event_action", "status", "error", "enqueued", "created", "started", "finished", "deploy", "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, "push", "", "", "", 0, 0, 0, 0, "", nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0). + AddRow(2, 2, nil, 2, 0, "push", "", "", "", 0, 0, 0, 0, "", nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0) + // ensure the mock expects the query with event filter + _mock.ExpectQuery(`SELECT builds.* FROM "builds" JOIN repos ON builds.repo_id = repos.id WHERE repos.org = $1 AND "event" = $2 ORDER BY created DESC,id LIMIT 10`).WithArgs("foo", "push").WillReturnRows(_rows) + + // create expected count query with visibility filter result in mock + _rows = sqlmock.NewRows([]string{"count"}).AddRow(2) + // ensure the mock expects the count query with visibility filter + _mock.ExpectQuery(`SELECT count(*) FROM "builds" JOIN repos ON builds.repo_id = repos.id WHERE repos.org = $1 AND "visibility" = $2`).WithArgs("foo", "public").WillReturnRows(_rows) + // create expected query with visibility filter result in mock + _rows = sqlmock.NewRows( + []string{"id", "repo_id", "pipeline_id", "number", "parent", "event", "event_action", "status", "error", "enqueued", "created", "started", "finished", "deploy", "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, "push", "", "", "", 0, 0, 0, 0, "", nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0). + AddRow(2, 2, nil, 2, 0, "push", "", "", "", 0, 0, 0, 0, "", nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0) + // ensure the mock expects the query with visibility filter + _mock.ExpectQuery(`SELECT builds.* FROM "builds" JOIN repos ON builds.repo_id = repos.id WHERE repos.org = $1 AND "visibility" = $2 ORDER BY created DESC,id LIMIT 10`).WithArgs("foo", "public").WillReturnRows(_rows) + + _sqlite := testSqlite(t) + defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() + + err := _sqlite.CreateBuild(_buildOne) + if err != nil { + t.Errorf("unable to create test build for sqlite: %v", err) + } + + err = _sqlite.CreateBuild(_buildTwo) + if err != nil { + t.Errorf("unable to create test build for sqlite: %v", err) + } + + err = _sqlite.client.AutoMigrate(&database.Repo{}) + if err != nil { + t.Errorf("unable to create repo table for sqlite: %v", err) + } + + err = _sqlite.client.Table(constants.TableRepo).Create(database.RepoFromLibrary(_repoOne)).Error + if err != nil { + t.Errorf("unable to create test repo for sqlite: %v", err) + } + + err = _sqlite.client.Table(constants.TableRepo).Create(database.RepoFromLibrary(_repoTwo)).Error + if err != nil { + t.Errorf("unable to create test repo for sqlite: %v", err) + } + + // setup tests + tests := []struct { + failure bool + name string + database *engine + filters map[string]interface{} + want []*library.Build + }{ + { + failure: false, + name: "postgres without filters", + database: _postgres, + filters: map[string]interface{}{}, + want: []*library.Build{_buildOne, _buildTwo}, + }, + { + failure: false, + name: "postgres with event filter", + database: _postgres, + filters: map[string]interface{}{ + "event": "push", + }, + want: []*library.Build{_buildOne, _buildTwo}, + }, + { + failure: false, + name: "postgres with visibility filter", + database: _postgres, + filters: map[string]interface{}{ + "visibility": "public", + }, + want: []*library.Build{_buildOne, _buildTwo}, + }, + { + failure: false, + name: "sqlite3 without filters", + database: _sqlite, + filters: map[string]interface{}{}, + want: []*library.Build{_buildOne, _buildTwo}, + }, + { + failure: false, + name: "sqlite3 with event filter", + database: _sqlite, + filters: map[string]interface{}{ + "event": "push", + }, + want: []*library.Build{_buildOne, _buildTwo}, + }, + { + failure: false, + name: "sqlite3 with visibility filter", + database: _sqlite, + filters: map[string]interface{}{ + "visibility": "public", + }, + want: []*library.Build{_buildOne, _buildTwo}, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, _, err := test.database.ListBuildsForOrg("foo", test.filters, 1, 10) + + if test.failure { + if err == nil { + t.Errorf("ListBuildsForOrg for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("ListBuildsForOrg for %s returned err: %v", test.name, err) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("ListBuildsForOrg for %s is %v, want %v", test.name, got, test.want) + } + }) + } +} diff --git a/database/build/list_pending_running.go b/database/build/list_pending_running.go new file mode 100644 index 000000000..bd2a219b5 --- /dev/null +++ b/database/build/list_pending_running.go @@ -0,0 +1,46 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "github.com/go-vela/types/constants" + "github.com/go-vela/types/database" + "github.com/go-vela/types/library" +) + +// ListPendingAndRunningBuilds gets a list of all pending and running builds in the provided timeframe from the database. +func (e *engine) ListPendingAndRunningBuilds(after string) ([]*library.BuildQueue, error) { + e.logger.Trace("listing all pending and running builds from the database") + + // variables to store query results and return value + b := new([]database.BuildQueue) + builds := []*library.BuildQueue{} + + // send query to the database and store result in variable + err := e.client. + Table(constants.TableBuild). + Select("builds.created, builds.number, builds.status, repos.full_name"). + InnerJoins("INNER JOIN repos ON builds.repo_id = repos.id"). + Where("builds.created > ?", after). + Where("builds.status = 'running' OR builds.status = 'pending'"). + Find(&b). + Error + if err != nil { + return nil, err + } + + // iterate through all query results + for _, build := range *b { + // https://golang.org/doc/faq#closures_and_goroutines + tmp := build + + // convert query result to library type + // + // https://pkg.go.dev/github.com/go-vela/types/database#Build.ToLibrary + builds = append(builds, tmp.ToLibrary()) + } + + return builds, nil +} diff --git a/database/build/list_pending_running_test.go b/database/build/list_pending_running_test.go new file mode 100644 index 000000000..8d47dc63b --- /dev/null +++ b/database/build/list_pending_running_test.go @@ -0,0 +1,131 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "reflect" + "testing" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/go-vela/types/constants" + "github.com/go-vela/types/database" + "github.com/go-vela/types/library" +) + +func TestBuild_Engine_ListPendingAndRunningBuilds(t *testing.T) { + // setup types + _buildOne := testBuild() + _buildOne.SetID(1) + _buildOne.SetRepoID(1) + _buildOne.SetNumber(1) + _buildOne.SetStatus("running") + _buildOne.SetCreated(1) + _buildOne.SetDeployPayload(nil) + + _buildTwo := testBuild() + _buildTwo.SetID(2) + _buildTwo.SetRepoID(1) + _buildTwo.SetNumber(2) + _buildTwo.SetStatus("pending") + _buildTwo.SetCreated(1) + _buildTwo.SetDeployPayload(nil) + + _queueOne := new(library.BuildQueue) + _queueOne.SetCreated(1) + _queueOne.SetFullName("foo/bar") + _queueOne.SetNumber(1) + _queueOne.SetStatus("running") + + _queueTwo := new(library.BuildQueue) + _queueTwo.SetCreated(1) + _queueTwo.SetFullName("foo/bar") + _queueTwo.SetNumber(2) + _queueTwo.SetStatus("pending") + + _repo := testRepo() + _repo.SetID(1) + _repo.SetUserID(1) + _repo.SetHash("baz") + _repo.SetOrg("foo") + _repo.SetName("bar") + _repo.SetFullName("foo/bar") + _repo.SetVisibility("public") + + _postgres, _mock := testPostgres(t) + defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() + + // create expected name query result in mock + _rows := sqlmock.NewRows([]string{"created", "full_name", "number", "status"}).AddRow(1, "foo/bar", 2, "pending").AddRow(1, "foo/bar", 1, "running") + + // ensure the mock expects the name query + _mock.ExpectQuery(`SELECT builds.created, builds.number, builds.status, repos.full_name FROM "builds" INNER JOIN repos ON builds.repo_id = repos.id WHERE builds.created > $1 AND (builds.status = 'running' OR builds.status = 'pending')`).WithArgs("0").WillReturnRows(_rows) + + _sqlite := testSqlite(t) + defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() + + err := _sqlite.CreateBuild(_buildOne) + if err != nil { + t.Errorf("unable to create test build for sqlite: %v", err) + } + + err = _sqlite.CreateBuild(_buildTwo) + if err != nil { + t.Errorf("unable to create test build for sqlite: %v", err) + } + + err = _sqlite.client.AutoMigrate(&database.Repo{}) + if err != nil { + t.Errorf("unable to create repo table for sqlite: %v", err) + } + + err = _sqlite.client.Table(constants.TableRepo).Create(database.RepoFromLibrary(_repo)).Error + if err != nil { + t.Errorf("unable to create test repo for sqlite: %v", err) + } + + // setup tests + tests := []struct { + failure bool + name string + database *engine + want []*library.BuildQueue + }{ + { + failure: false, + name: "postgres", + database: _postgres, + want: []*library.BuildQueue{_queueTwo, _queueOne}, + }, + { + failure: false, + name: "sqlite3", + database: _sqlite, + want: []*library.BuildQueue{_queueTwo, _queueOne}, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, err := test.database.ListPendingAndRunningBuilds("0") + + if test.failure { + if err == nil { + t.Errorf("ListPendingAndRunningBuilds for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("ListPendingAndRunningBuilds for %s returned err: %v", test.name, err) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("ListPendingAndRunningBuilds for %s is %v, want %v", test.name, got, test.want) + } + }) + } +} diff --git a/database/build/list_repo.go b/database/build/list_repo.go new file mode 100644 index 000000000..83ebd4adb --- /dev/null +++ b/database/build/list_repo.go @@ -0,0 +1,69 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "github.com/go-vela/types/constants" + "github.com/go-vela/types/database" + "github.com/go-vela/types/library" + "github.com/sirupsen/logrus" +) + +// ListBuildsForRepo gets a list of builds by repo ID from the database. +// +//nolint:lll // ignore long line length due to variable names +func (e *engine) ListBuildsForRepo(r *library.Repo, filters map[string]interface{}, before, after int64, page, perPage int) ([]*library.Build, int64, error) { + e.logger.WithFields(logrus.Fields{ + "org": r.GetOrg(), + "repo": r.GetName(), + }).Tracef("listing builds for repo %s from the database", r.GetFullName()) + + // variables to store query results and return values + count := int64(0) + b := new([]database.Build) + builds := []*library.Build{} + + // count the results + count, err := e.CountBuildsForRepo(r, filters) + if err != nil { + return builds, 0, err + } + + // short-circuit if there are no results + if count == 0 { + return builds, 0, nil + } + + // calculate offset for pagination through results + offset := perPage * (page - 1) + + err = e.client. + Table(constants.TableBuild). + Where("repo_id = ?", r.GetID()). + Where("created < ?", before). + Where("created > ?", after). + Where(filters). + Order("number DESC"). + Limit(perPage). + Offset(offset). + Find(&b). + Error + if err != nil { + return nil, count, err + } + + // iterate through all query results + for _, build := range *b { + // https://golang.org/doc/faq#closures_and_goroutines + tmp := build + + // convert query result to library type + // + // https://pkg.go.dev/github.com/go-vela/types/database#Build.ToLibrary + builds = append(builds, tmp.ToLibrary()) + } + + return builds, count, nil +} diff --git a/database/build/list_repo_test.go b/database/build/list_repo_test.go new file mode 100644 index 000000000..ac367e414 --- /dev/null +++ b/database/build/list_repo_test.go @@ -0,0 +1,117 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "reflect" + "testing" + "time" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/go-vela/types/library" +) + +func TestBuild_Engine_ListBuildsForRepo(t *testing.T) { + // setup types + _buildOne := testBuild() + _buildOne.SetID(1) + _buildOne.SetRepoID(1) + _buildOne.SetNumber(1) + _buildOne.SetDeployPayload(nil) + _buildOne.SetCreated(1) + + _buildTwo := testBuild() + _buildTwo.SetID(2) + _buildTwo.SetRepoID(1) + _buildTwo.SetNumber(2) + _buildTwo.SetDeployPayload(nil) + _buildTwo.SetCreated(2) + + _repo := testRepo() + _repo.SetID(1) + _repo.SetUserID(1) + _repo.SetHash("baz") + _repo.SetOrg("foo") + _repo.SetName("bar") + _repo.SetFullName("foo/bar") + _repo.SetVisibility("public") + + _postgres, _mock := testPostgres(t) + defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() + + // create expected count query result in mock + _rows := sqlmock.NewRows([]string{"count"}).AddRow(2) + + // ensure the mock expects the count query + _mock.ExpectQuery(`SELECT count(*) FROM "builds" WHERE repo_id = $1`).WithArgs(1).WillReturnRows(_rows) + + // create expected query result in mock + _rows = sqlmock.NewRows( + []string{"id", "repo_id", "pipeline_id", "number", "parent", "event", "event_action", "status", "error", "enqueued", "created", "started", "finished", "deploy", "deploy_payload", "clone", "source", "title", "message", "commit", "sender", "author", "email", "link", "branch", "ref", "base_ref", "head_ref", "host", "runtime", "distribution", "timestamp"}). + AddRow(2, 1, nil, 2, 0, "", "", "", "", 0, 2, 0, 0, "", nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0). + AddRow(1, 1, nil, 1, 0, "", "", "", "", 0, 1, 0, 0, "", nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0) + + // ensure the mock expects the query + _mock.ExpectQuery(`SELECT * FROM "builds" WHERE repo_id = $1 AND created < $2 AND created > $3 ORDER BY number DESC LIMIT 10`).WithArgs(1, AnyArgument{}, 0).WillReturnRows(_rows) + + _sqlite := testSqlite(t) + defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() + + err := _sqlite.CreateBuild(_buildOne) + if err != nil { + t.Errorf("unable to create test build for sqlite: %v", err) + } + + err = _sqlite.CreateBuild(_buildTwo) + if err != nil { + t.Errorf("unable to create test build for sqlite: %v", err) + } + + // setup tests + tests := []struct { + failure bool + name string + database *engine + want []*library.Build + }{ + { + failure: false, + name: "postgres", + database: _postgres, + want: []*library.Build{_buildTwo, _buildOne}, + }, + { + failure: false, + name: "sqlite3", + database: _sqlite, + want: []*library.Build{_buildTwo, _buildOne}, + }, + } + + filters := map[string]interface{}{} + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, _, err := test.database.ListBuildsForRepo(_repo, filters, time.Now().UTC().Unix(), 0, 1, 10) + + if test.failure { + if err == nil { + t.Errorf("ListBuildsForRepo for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("ListBuildsForRepo for %s returned err: %v", test.name, err) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("ListBuildsForRepo for %s is %v, want %v", test.name, got, test.want) + } + }) + } +} diff --git a/database/build/list_test.go b/database/build/list_test.go new file mode 100644 index 000000000..b0e441387 --- /dev/null +++ b/database/build/list_test.go @@ -0,0 +1,103 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "reflect" + "testing" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/go-vela/types/library" +) + +func TestBuild_Engine_ListBuilds(t *testing.T) { + // setup types + _buildOne := testBuild() + _buildOne.SetID(1) + _buildOne.SetRepoID(1) + _buildOne.SetNumber(1) + _buildOne.SetDeployPayload(nil) + + _buildTwo := testBuild() + _buildTwo.SetID(2) + _buildTwo.SetRepoID(1) + _buildTwo.SetNumber(2) + _buildTwo.SetDeployPayload(nil) + + _postgres, _mock := testPostgres(t) + defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() + + // create expected result in mock + _rows := sqlmock.NewRows([]string{"count"}).AddRow(2) + + // ensure the mock expects the query + _mock.ExpectQuery(`SELECT count(*) FROM "builds"`).WillReturnRows(_rows) + + // create expected result in mock + _rows = sqlmock.NewRows( + []string{"id", "repo_id", "pipeline_id", "number", "parent", "event", "event_action", "status", "error", "enqueued", "created", "started", "finished", "deploy", "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, "", nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0). + AddRow(2, 1, nil, 2, 0, "", "", "", "", 0, 0, 0, 0, "", nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0) + + // ensure the mock expects the query + _mock.ExpectQuery(`SELECT * FROM "builds"`).WillReturnRows(_rows) + + _sqlite := testSqlite(t) + defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() + + err := _sqlite.CreateBuild(_buildOne) + if err != nil { + t.Errorf("unable to create test build for sqlite: %v", err) + } + + err = _sqlite.CreateBuild(_buildTwo) + if err != nil { + t.Errorf("unable to create test build for sqlite: %v", err) + } + + // setup tests + tests := []struct { + failure bool + name string + database *engine + want []*library.Build + }{ + { + failure: false, + name: "postgres", + database: _postgres, + want: []*library.Build{_buildOne, _buildTwo}, + }, + { + failure: false, + name: "sqlite3", + database: _sqlite, + want: []*library.Build{_buildOne, _buildTwo}, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, err := test.database.ListBuilds() + + if test.failure { + if err == nil { + t.Errorf("ListBuilds for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("ListBuilds for %s returned err: %v", test.name, err) + } + + if !reflect.DeepEqual(got, test.want) { + t.Errorf("ListBuilds for %s is %v, want %v", test.name, got, test.want) + } + }) + } +} diff --git a/database/build/opts.go b/database/build/opts.go new file mode 100644 index 000000000..d5a5d4137 --- /dev/null +++ b/database/build/opts.go @@ -0,0 +1,44 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "github.com/sirupsen/logrus" + + "gorm.io/gorm" +) + +// EngineOpt represents a configuration option to initialize the database engine for Builds. +type EngineOpt func(*engine) error + +// WithClient sets the gorm.io/gorm client in the database engine for Builds. +func WithClient(client *gorm.DB) EngineOpt { + return func(e *engine) error { + // set the gorm.io/gorm client in the build engine + e.client = client + + return nil + } +} + +// WithLogger sets the github.com/sirupsen/logrus logger in the database engine for Builds. +func WithLogger(logger *logrus.Entry) EngineOpt { + return func(e *engine) error { + // set the github.com/sirupsen/logrus logger in the build engine + e.logger = logger + + return nil + } +} + +// WithSkipCreation sets the skip creation logic in the database engine for Builds. +func WithSkipCreation(skipCreation bool) EngineOpt { + return func(e *engine) error { + // set to skip creating tables and indexes in the build engine + e.config.SkipCreation = skipCreation + + return nil + } +} diff --git a/database/build/opts_test.go b/database/build/opts_test.go new file mode 100644 index 000000000..2d41cc676 --- /dev/null +++ b/database/build/opts_test.go @@ -0,0 +1,161 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "reflect" + "testing" + + "github.com/sirupsen/logrus" + + "gorm.io/gorm" +) + +func TestBuild_EngineOpt_WithClient(t *testing.T) { + // setup types + e := &engine{client: new(gorm.DB)} + + // setup tests + tests := []struct { + failure bool + name string + client *gorm.DB + want *gorm.DB + }{ + { + failure: false, + name: "client set to new database", + client: new(gorm.DB), + want: new(gorm.DB), + }, + { + failure: false, + name: "client set to nil", + client: nil, + want: nil, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + err := WithClient(test.client)(e) + + if test.failure { + if err == nil { + t.Errorf("WithClient for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("WithClient returned err: %v", err) + } + + if !reflect.DeepEqual(e.client, test.want) { + t.Errorf("WithClient is %v, want %v", e.client, test.want) + } + }) + } +} + +func TestBuild_EngineOpt_WithLogger(t *testing.T) { + // setup types + e := &engine{logger: new(logrus.Entry)} + + // setup tests + tests := []struct { + failure bool + name string + logger *logrus.Entry + want *logrus.Entry + }{ + { + failure: false, + name: "logger set to new entry", + logger: new(logrus.Entry), + want: new(logrus.Entry), + }, + { + failure: false, + name: "logger set to nil", + logger: nil, + want: nil, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + err := WithLogger(test.logger)(e) + + if test.failure { + if err == nil { + t.Errorf("WithLogger for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("WithLogger returned err: %v", err) + } + + if !reflect.DeepEqual(e.logger, test.want) { + t.Errorf("WithLogger is %v, want %v", e.logger, test.want) + } + }) + } +} + +func TestBuild_EngineOpt_WithSkipCreation(t *testing.T) { + // setup types + e := &engine{config: new(config)} + + // setup tests + tests := []struct { + failure bool + name string + skipCreation bool + want bool + }{ + { + failure: false, + name: "skip creation set to true", + skipCreation: true, + want: true, + }, + { + failure: false, + name: "skip creation set to false", + skipCreation: false, + want: false, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + err := WithSkipCreation(test.skipCreation)(e) + + if test.failure { + if err == nil { + t.Errorf("WithSkipCreation for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("WithSkipCreation returned err: %v", err) + } + + if !reflect.DeepEqual(e.config.SkipCreation, test.want) { + t.Errorf("WithSkipCreation is %v, want %v", e.config.SkipCreation, test.want) + } + }) + } +} diff --git a/database/build/table.go b/database/build/table.go new file mode 100644 index 000000000..faf76746f --- /dev/null +++ b/database/build/table.go @@ -0,0 +1,108 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import "github.com/go-vela/types/constants" + +const ( + // CreatePostgresTable represents a query to create the Postgres builds table. + CreatePostgresTable = ` +CREATE TABLE +IF NOT EXISTS +builds ( + id SERIAL PRIMARY KEY, + repo_id INTEGER, + pipeline_id INTEGER, + number INTEGER, + parent INTEGER, + event VARCHAR(250), + event_action VARCHAR(250), + status VARCHAR(250), + error VARCHAR(1000), + enqueued INTEGER, + created INTEGER, + started INTEGER, + finished INTEGER, + deploy VARCHAR(500), + deploy_payload VARCHAR(2000), + clone VARCHAR(1000), + source VARCHAR(1000), + title VARCHAR(1000), + message VARCHAR(2000), + commit VARCHAR(500), + sender VARCHAR(250), + author VARCHAR(250), + email VARCHAR(500), + link VARCHAR(1000), + branch VARCHAR(500), + ref VARCHAR(500), + base_ref VARCHAR(500), + head_ref VARCHAR(500), + host VARCHAR(250), + runtime VARCHAR(250), + distribution VARCHAR(250), + timestamp INTEGER, + UNIQUE(repo_id, number) +); +` + + // CreateSqliteTable represents a query to create the Sqlite builds table. + CreateSqliteTable = ` +CREATE TABLE +IF NOT EXISTS +builds ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + repo_id INTEGER, + pipeline_id INTEGER, + number INTEGER, + parent INTEGER, + event TEXT, + event_action TEXT, + status TEXT, + error TEXT, + enqueued INTEGER, + created INTEGER, + started INTEGER, + finished INTEGER, + deploy TEXT, + deploy_payload TEXT, + clone TEXT, + source TEXT, + title TEXT, + message TEXT, + 'commit' TEXT, + sender TEXT, + author TEXT, + email TEXT, + link TEXT, + branch TEXT, + ref TEXT, + base_ref TEXT, + head_ref TEXT, + host TEXT, + runtime TEXT, + distribution TEXT, + timestamp INTEGER, + UNIQUE(repo_id, number) +); +` +) + +// CreateBuildTable creates the builds table in the database. +func (e *engine) CreateBuildTable(driver string) error { + e.logger.Tracef("creating builds table in the database") + + // handle the driver provided to create the table + switch driver { + case constants.DriverPostgres: + // create the builds table for Postgres + return e.client.Exec(CreatePostgresTable).Error + case constants.DriverSqlite: + fallthrough + default: + // create the builds table for Sqlite + return e.client.Exec(CreateSqliteTable).Error + } +} diff --git a/database/build/table_test.go b/database/build/table_test.go new file mode 100644 index 000000000..394a1287e --- /dev/null +++ b/database/build/table_test.go @@ -0,0 +1,59 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "testing" + + "github.com/DATA-DOG/go-sqlmock" +) + +func TestBuild_Engine_CreateBuildTable(t *testing.T) { + // setup types + _postgres, _mock := testPostgres(t) + defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() + + _mock.ExpectExec(CreatePostgresTable).WillReturnResult(sqlmock.NewResult(1, 1)) + + _sqlite := testSqlite(t) + defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() + + // setup tests + tests := []struct { + failure bool + name string + database *engine + }{ + { + failure: false, + name: "postgres", + database: _postgres, + }, + { + failure: false, + name: "sqlite3", + database: _sqlite, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + err := test.database.CreateBuildTable(test.name) + + if test.failure { + if err == nil { + t.Errorf("CreateBuildTable for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("CreateBuildTable for %s returned err: %v", test.name, err) + } + }) + } +} diff --git a/database/build/update.go b/database/build/update.go new file mode 100644 index 000000000..d16329419 --- /dev/null +++ b/database/build/update.go @@ -0,0 +1,39 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +//nolint:dupl // ignore similar code with create.go +package build + +import ( + "github.com/go-vela/types/constants" + "github.com/go-vela/types/database" + "github.com/go-vela/types/library" + "github.com/sirupsen/logrus" +) + +// UpdateBuild updates an existing build in the database. +func (e *engine) UpdateBuild(b *library.Build) error { + e.logger.WithFields(logrus.Fields{ + "build": b.GetNumber(), + }).Tracef("updating build %d in the database", b.GetNumber()) + + // cast the library type to database type + // + // https://pkg.go.dev/github.com/go-vela/types/database#BuildFromLibrary + build := database.BuildFromLibrary(b) + + // validate the necessary fields are populated + // + // https://pkg.go.dev/github.com/go-vela/types/database#Build.Validate + err := build.Validate() + if err != nil { + return err + } + + // send query to the database + return e.client. + Table(constants.TableBuild). + Save(build.Crop()). + Error +} diff --git a/database/build/update_test.go b/database/build/update_test.go new file mode 100644 index 000000000..98ac9ae16 --- /dev/null +++ b/database/build/update_test.go @@ -0,0 +1,75 @@ +// Copyright (c) 2023 Target Brands, Inc. All rights reserved. +// +// Use of this source code is governed by the LICENSE file in this repository. + +package build + +import ( + "testing" + + "github.com/DATA-DOG/go-sqlmock" +) + +func TestBuild_Engine_UpdateBuild(t *testing.T) { + // setup types + _build := testBuild() + _build.SetID(1) + _build.SetRepoID(1) + _build.SetNumber(1) + _build.SetDeployPayload(nil) + + _postgres, _mock := testPostgres(t) + defer func() { _sql, _ := _postgres.client.DB(); _sql.Close() }() + + // ensure the mock expects the query + _mock.ExpectExec(`UPDATE "builds" +SET "repo_id"=$1,"pipeline_id"=$2,"number"=$3,"parent"=$4,"event"=$5,"event_action"=$6,"status"=$7,"error"=$8,"enqueued"=$9,"created"=$10,"started"=$11,"finished"=$12,"deploy"=$13,"deploy_payload"=$14,"clone"=$15,"source"=$16,"title"=$17,"message"=$18,"commit"=$19,"sender"=$20,"author"=$21,"email"=$22,"link"=$23,"branch"=$24,"ref"=$25,"base_ref"=$26,"head_ref"=$27,"host"=$28,"runtime"=$29,"distribution"=$30 +WHERE "id" = $31`). + WithArgs(1, nil, 1, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, AnyArgument{}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 1). + WillReturnResult(sqlmock.NewResult(1, 1)) + + _sqlite := testSqlite(t) + defer func() { _sql, _ := _sqlite.client.DB(); _sql.Close() }() + + err := _sqlite.CreateBuild(_build) + if err != nil { + t.Errorf("unable to create test build for sqlite: %v", err) + } + + // setup tests + tests := []struct { + failure bool + name string + database *engine + }{ + { + failure: false, + name: "postgres", + database: _postgres, + }, + { + failure: false, + name: "sqlite3", + database: _sqlite, + }, + } + + // run tests + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + err = test.database.UpdateBuild(_build) + + if test.failure { + if err == nil { + t.Errorf("UpdateBuild for %s should have returned err", test.name) + } + + return + } + + if err != nil { + t.Errorf("UpdateBuild for %s returned err: %v", test.name, err) + } + }) + } +} diff --git a/database/interface.go b/database/interface.go index 9a2177b14..975206c93 100644 --- a/database/interface.go +++ b/database/interface.go @@ -5,6 +5,7 @@ package database import ( + "github.com/go-vela/server/database/build" "github.com/go-vela/server/database/hook" "github.com/go-vela/server/database/log" "github.com/go-vela/server/database/pipeline" @@ -15,7 +16,6 @@ import ( "github.com/go-vela/server/database/step" "github.com/go-vela/server/database/user" "github.com/go-vela/server/database/worker" - "github.com/go-vela/types/library" ) // Interface represents the interface for Vela integrating @@ -27,94 +27,36 @@ type Interface interface { // the configured database driver. Driver() string - // Build Database Interface Functions + // BuildInterface defines the interface for builds stored in the database. + build.BuildInterface - // GetBuild defines a function that - // gets a build by number and repo ID. - GetBuild(int, *library.Repo) (*library.Build, error) - // GetBuildByID defines a function that - // gets a build by its id. - GetBuildByID(int64) (*library.Build, error) - // GetLastBuild defines a function that - // gets the last build ran by repo ID. - GetLastBuild(*library.Repo) (*library.Build, error) - // GetLastBuildByBranch defines a function that - // gets the last build ran by repo ID and branch. - GetLastBuildByBranch(*library.Repo, string) (*library.Build, error) - // GetBuildCount defines a function that - // gets the count of builds. - GetBuildCount() (int64, error) - // GetBuildCountByStatus defines a function that - // gets a the count of builds by status. - GetBuildCountByStatus(string) (int64, error) - // GetBuildList defines a function that gets - // a list of all builds. - GetBuildList() ([]*library.Build, error) - // GetDeploymentBuildList defines a function that gets - // a list of builds related to a deployment. - GetDeploymentBuildList(string) ([]*library.Build, error) - // GetRepoBuildList defines a function that - // gets a list of builds by repo ID. - GetRepoBuildList(*library.Repo, map[string]interface{}, int64, int64, int, int) ([]*library.Build, int64, error) - // GetOrgBuildList defines a function that - // gets a list of builds by org. - GetOrgBuildList(string, map[string]interface{}, int, int) ([]*library.Build, int64, error) - // GetRepoBuildCount defines a function that - // gets the count of builds by repo ID. - GetRepoBuildCount(*library.Repo, map[string]interface{}) (int64, error) - // GetOrgBuildCount defines a function that - // gets the count of builds by org. - GetOrgBuildCount(string, map[string]interface{}) (int64, error) - // GetPendingAndRunningBuilds defines a function that - // gets the list of pending and running builds. - GetPendingAndRunningBuilds(string) ([]*library.BuildQueue, error) - // CreateBuild defines a function that - // creates a new build. - CreateBuild(*library.Build) error - // UpdateBuild defines a function that - // updates a build. - UpdateBuild(*library.Build) error - // DeleteBuild defines a function that - // deletes a build by unique ID. - DeleteBuild(int64) error - - // HookInterface provides the interface for functionality - // related to hooks stored in the database. + // HookInterface defines the interface for hooks stored in the database. hook.HookInterface - // LogInterface provides the interface for functionality - // related to logs stored in the database. + // LogInterface defines the interface for logs stored in the database. log.LogInterface - // PipelineInterface provides the interface for functionality - // related to pipelines stored in the database. + // PipelineInterface defines the interface for pipelines stored in the database. pipeline.PipelineInterface - // RepoInterface provides the interface for functionality - // related to repos stored in the database. + // RepoInterface defines the interface for repos stored in the database. repo.RepoInterface - // ScheduleInterface provides the interface for functionality - // related to schedules stored in the database. + // ScheduleInterface defines the interface for schedules stored in the database. schedule.ScheduleInterface - // SecretInterface provides the interface for functionality - // related to secrets stored in the database. + // SecretInterface defines the interface for secrets stored in the database. secret.SecretInterface - // ServiceInterface provides the interface for functionality - // related to services stored in the database. + // ServiceInterface defines the interface for services stored in the database. service.ServiceInterface - // StepInterface provides the interface for functionality - // related to steps stored in the database. + // StepInterface defines the interface for steps stored in the database. step.StepInterface - // UserInterface provides the interface for functionality - // related to users stored in the database. + // UserInterface defines the interface for users stored in the database. user.UserInterface - // WorkerInterface provides the interface for functionality - // related to workers stored in the database. + // WorkerInterface defines the interface for workers stored in the database. worker.WorkerInterface } diff --git a/database/postgres/build.go b/database/postgres/build.go deleted file mode 100644 index c8b7b66df..000000000 --- a/database/postgres/build.go +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright (c) 2022 Target Brands, Inc. All rights reserved. -// -// Use of this source code is governed by the LICENSE file in this repository. - -package postgres - -import ( - "errors" - - "github.com/sirupsen/logrus" - - "github.com/go-vela/server/database/postgres/dml" - "github.com/go-vela/types/constants" - "github.com/go-vela/types/database" - "github.com/go-vela/types/library" - - "gorm.io/gorm" -) - -// GetBuild gets a build by number and repo ID from the database. -func (c *client) GetBuild(number int, r *library.Repo) (*library.Build, error) { - c.Logger.WithFields(logrus.Fields{ - "build": number, - "org": r.GetOrg(), - "repo": r.GetName(), - }).Tracef("getting build %s/%d from the database", r.GetFullName(), number) - - // variable to store query results - b := new(database.Build) - - // send query to the database and store result in variable - result := c.Postgres. - Table(constants.TableBuild). - Raw(dml.SelectRepoBuild, r.GetID(), number). - Scan(b) - - // check if the query returned a record not found error or no rows were returned - if errors.Is(result.Error, gorm.ErrRecordNotFound) || result.RowsAffected == 0 { - return nil, gorm.ErrRecordNotFound - } - - return b.ToLibrary(), result.Error -} - -// GetBuildByID gets a build by id from the database. -func (c *client) GetBuildByID(id int64) (*library.Build, error) { - c.Logger.WithFields(logrus.Fields{ - "build": id, - }).Tracef("getting build %d from the database", id) - - // variable to store query result - b := new(database.Build) - - // send query to the database and store result in variable - result := c.Postgres. - Table(constants.TableBuild). - Raw(dml.SelectBuildByID, id). - Scan(b) - - // check if the query returned a record not found error or no rows were returned - if errors.Is(result.Error, gorm.ErrRecordNotFound) || result.RowsAffected == 0 { - return nil, gorm.ErrRecordNotFound - } - - return b.ToLibrary(), result.Error -} - -// GetLastBuild gets the last build by repo ID from the database. -func (c *client) GetLastBuild(r *library.Repo) (*library.Build, error) { - c.Logger.WithFields(logrus.Fields{ - "org": r.GetOrg(), - "repo": r.GetName(), - }).Tracef("getting last build for repo %s from the database", r.GetFullName()) - - // variable to store query results - b := new(database.Build) - - // send query to the database and store result in variable - result := c.Postgres. - Table(constants.TableBuild). - Raw(dml.SelectLastRepoBuild, r.GetID()). - Scan(b) - - // check if the query returned a record not found error or no rows were returned - if errors.Is(result.Error, gorm.ErrRecordNotFound) || result.RowsAffected == 0 { - // the record will not exist if it's a new repo - return nil, nil - } - - return b.ToLibrary(), result.Error -} - -// GetLastBuildByBranch gets the last build by repo ID and branch from the database. -func (c *client) GetLastBuildByBranch(r *library.Repo, branch string) (*library.Build, error) { - c.Logger.WithFields(logrus.Fields{ - "org": r.GetOrg(), - "repo": r.GetName(), - }).Tracef("getting last build for repo %s on branch %s from the database", r.GetFullName(), branch) - - // variable to store query results - b := new(database.Build) - - // send query to the database and store result in variable - result := c.Postgres. - Table(constants.TableBuild). - Raw(dml.SelectLastRepoBuildByBranch, r.GetID(), branch). - Scan(b) - - // check if the query returned a record not found error or no rows were returned - if errors.Is(result.Error, gorm.ErrRecordNotFound) || result.RowsAffected == 0 { - // the record will not exist if it's a new repo - return nil, nil - } - - return b.ToLibrary(), result.Error -} - -// GetPendingAndRunningBuilds returns the list of pending -// and running builds within the given timeframe. -func (c *client) GetPendingAndRunningBuilds(after string) ([]*library.BuildQueue, error) { - c.Logger.Trace("getting pending and running builds from the database") - - // variable to store query results - b := new([]database.BuildQueue) - - // send query to the database and store result in variable - result := c.Postgres. - Table(constants.TableBuild). - Raw(dml.SelectPendingAndRunningBuilds, after). - Scan(b) - - // variable we want to return - builds := []*library.BuildQueue{} - - // iterate through all query results - for _, build := range *b { - // https://golang.org/doc/faq#closures_and_goroutines - tmp := build - - // convert query result to library type - builds = append(builds, tmp.ToLibrary()) - } - - return builds, result.Error -} - -// CreateBuild creates a new build in the database. -func (c *client) CreateBuild(b *library.Build) error { - c.Logger.WithFields(logrus.Fields{ - "build": b.GetNumber(), - }).Tracef("creating build %d in the database", b.GetNumber()) - - // cast to database type - build := database.BuildFromLibrary(b) - - // validate the necessary fields are populated - err := build.Validate() - if err != nil { - return err - } - - // send query to the database - return c.Postgres. - Table(constants.TableBuild). - Create(build.Crop()).Error -} - -// UpdateBuild updates a build in the database. -func (c *client) UpdateBuild(b *library.Build) error { - c.Logger.WithFields(logrus.Fields{ - "build": b.GetNumber(), - }).Tracef("updating build %d in the database", b.GetNumber()) - - // cast to database type - build := database.BuildFromLibrary(b) - - // validate the necessary fields are populated - err := build.Validate() - if err != nil { - return err - } - - // send query to the database - return c.Postgres. - Table(constants.TableBuild). - Save(build.Crop()).Error -} - -// DeleteBuild deletes a build by unique ID from the database. -func (c *client) DeleteBuild(id int64) error { - c.Logger.Tracef("deleting build %d in the database", id) - - // send query to the database - return c.Postgres. - Table(constants.TableBuild). - Exec(dml.DeleteBuild, id).Error -} diff --git a/database/postgres/build_count.go b/database/postgres/build_count.go deleted file mode 100644 index 0d7af8f5f..000000000 --- a/database/postgres/build_count.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) 2022 Target Brands, Inc. All rights reserved. -// -// Use of this source code is governed by the LICENSE file in this repository. - -package postgres - -import ( - "github.com/go-vela/server/database/postgres/dml" - "github.com/go-vela/types/constants" - "github.com/go-vela/types/library" - "github.com/sirupsen/logrus" -) - -// GetBuildCount gets a count of all builds from the database. -func (c *client) GetBuildCount() (int64, error) { - c.Logger.Trace("getting count of builds from the database") - - // variable to store query results - var b int64 - - // send query to the database and store result in variable - err := c.Postgres. - Table(constants.TableBuild). - Raw(dml.SelectBuildsCount). - Pluck("count", &b).Error - - return b, err -} - -// GetBuildCountByStatus gets a count of all builds by status from the database. -func (c *client) GetBuildCountByStatus(status string) (int64, error) { - c.Logger.Tracef("getting count of builds by status %s from the database", status) - - // variable to store query results - var b int64 - - // send query to the database and store result in variable - err := c.Postgres. - Table(constants.TableBuild). - Raw(dml.SelectBuildsCountByStatus, status). - Pluck("count", &b).Error - - return b, err -} - -// GetOrgBuildCount gets the count of all builds by repo ID from the database. -func (c *client) GetOrgBuildCount(org string, filters map[string]interface{}) (int64, error) { - c.Logger.WithFields(logrus.Fields{ - "org": org, - }).Tracef("getting count of builds for org %s from the database", org) - - // variable to store query results - var b int64 - - // send query to the database and store result in variable - err := c.Postgres. - Table(constants.TableBuild). - Joins("JOIN repos ON builds.repo_id = repos.id and repos.org = ?", org). - Where(filters). - Count(&b).Error - - return b, err -} - -// GetRepoBuildCount gets the count of all builds by repo ID from the database. -func (c *client) GetRepoBuildCount(r *library.Repo, filters map[string]interface{}) (int64, error) { - c.Logger.WithFields(logrus.Fields{ - "org": r.GetOrg(), - "name": r.GetName(), - }).Tracef("getting count of builds for repo %s from the database", r.GetFullName()) - - // variable to store query results - var b int64 - - // send query to the database and store result in variable - err := c.Postgres. - Table(constants.TableBuild). - Where("repo_id = ?", r.GetID()). - Where(filters). - Count(&b).Error - - return b, err -} diff --git a/database/postgres/build_count_test.go b/database/postgres/build_count_test.go deleted file mode 100644 index 0c4522798..000000000 --- a/database/postgres/build_count_test.go +++ /dev/null @@ -1,345 +0,0 @@ -// Copyright (c) 2022 Target Brands, Inc. All rights reserved. -// -// Use of this source code is governed by the LICENSE file in this repository. - -package postgres - -import ( - "reflect" - "testing" - - "github.com/DATA-DOG/go-sqlmock" - - "github.com/go-vela/server/database/postgres/dml" - - "gorm.io/gorm" -) - -func TestPostgres_Client_GetBuildCount(t *testing.T) { - // setup types - _buildOne := testBuild() - _buildOne.SetID(1) - _buildOne.SetRepoID(1) - _buildOne.SetNumber(1) - _buildOne.SetDeployPayload(nil) - - _buildTwo := testBuild() - _buildTwo.SetID(2) - _buildTwo.SetRepoID(1) - _buildTwo.SetNumber(2) - _buildTwo.SetDeployPayload(nil) - - // setup the test database client - _database, _mock, err := NewTest() - if err != nil { - t.Errorf("unable to create new postgres test database: %v", err) - } - - defer func() { _sql, _ := _database.Postgres.DB(); _sql.Close() }() - - // capture the current expected SQL query - // - // https://gorm.io/docs/sql_builder.html#DryRun-Mode - _query := _database.Postgres.Session(&gorm.Session{DryRun: true}).Raw(dml.SelectBuildsCount).Statement - - // create expected return in mock - _rows := sqlmock.NewRows([]string{"count"}).AddRow(2) - - // ensure the mock expects the query - _mock.ExpectQuery(_query.SQL.String()).WillReturnRows(_rows) - - // setup tests - tests := []struct { - failure bool - want int64 - }{ - { - failure: false, - want: 2, - }, - } - - // run tests - for _, test := range tests { - got, err := _database.GetBuildCount() - - if test.failure { - if err == nil { - t.Errorf("GetBuildCount should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("GetBuildCount returned err: %v", err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetBuildCount is %v, want %v", got, test.want) - } - } -} - -func TestPostgres_Client_GetBuildCountByStatus(t *testing.T) { - // setup types - _buildOne := testBuild() - _buildOne.SetID(1) - _buildOne.SetRepoID(1) - _buildOne.SetNumber(1) - _buildOne.SetDeployPayload(nil) - - _buildTwo := testBuild() - _buildTwo.SetID(2) - _buildTwo.SetRepoID(1) - _buildTwo.SetNumber(2) - _buildTwo.SetDeployPayload(nil) - - // setup the test database client - _database, _mock, err := NewTest() - if err != nil { - t.Errorf("unable to create new postgres test database: %v", err) - } - - defer func() { _sql, _ := _database.Postgres.DB(); _sql.Close() }() - - // capture the current expected SQL query - // - // https://gorm.io/docs/sql_builder.html#DryRun-Mode - _query := _database.Postgres.Session(&gorm.Session{DryRun: true}).Raw(dml.SelectBuildsCountByStatus, "running").Statement - - // create expected return in mock - _rows := sqlmock.NewRows([]string{"count"}).AddRow(2) - - // ensure the mock expects the query - _mock.ExpectQuery(_query.SQL.String()).WillReturnRows(_rows) - - // setup tests - tests := []struct { - failure bool - want int64 - }{ - { - failure: false, - want: 2, - }, - } - - // run tests - for _, test := range tests { - got, err := _database.GetBuildCountByStatus("running") - - if test.failure { - if err == nil { - t.Errorf("GetBuildCountByStatus should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("GetBuildCountByStatus returned err: %v", err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetBuildCountByStatus is %v, want %v", got, test.want) - } - } -} - -func TestPostgres_Client_GetOrgBuildCount(t *testing.T) { - // setup types - _buildOne := testBuild() - _buildOne.SetID(1) - _buildOne.SetRepoID(1) - _buildOne.SetNumber(1) - _buildOne.SetDeployPayload(nil) - - _buildTwo := testBuild() - _buildTwo.SetID(2) - _buildTwo.SetRepoID(1) - _buildTwo.SetNumber(2) - _buildTwo.SetDeployPayload(nil) - - // setup the test database client - _database, _mock, err := NewTest() - if err != nil { - t.Errorf("unable to create new postgres test database: %v", err) - } - - defer func() { _sql, _ := _database.Postgres.DB(); _sql.Close() }() - - // create expected return in mock - _rows := sqlmock.NewRows([]string{"count"}).AddRow(2) - - // ensure the mock expects the query - _mock.ExpectQuery("SELECT count(*) FROM \"builds\" JOIN repos ON builds.repo_id = repos.id and repos.org = $1").WillReturnRows(_rows) - - // setup tests - tests := []struct { - failure bool - want int64 - }{ - { - failure: false, - want: 2, - }, - } - filters := map[string]interface{}{} - // run tests - for _, test := range tests { - got, err := _database.GetOrgBuildCount("foo", filters) - - if test.failure { - if err == nil { - t.Errorf("GetOrgBuildCount should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("GetOrgBuildCount returned err: %v", err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetOrgBuildCount is %v, want %v", got, test.want) - } - } -} - -func TestPostgres_Client_GetOrgBuildCountByEvent(t *testing.T) { - // setup types - _buildOne := testBuild() - _buildOne.SetID(1) - _buildOne.SetRepoID(1) - _buildOne.SetNumber(1) - _buildOne.SetDeployPayload(nil) - - _buildTwo := testBuild() - _buildTwo.SetID(2) - _buildTwo.SetRepoID(1) - _buildTwo.SetNumber(2) - _buildTwo.SetDeployPayload(nil) - - // setup the test database client - _database, _mock, err := NewTest() - if err != nil { - t.Errorf("unable to create new postgres test database: %v", err) - } - - defer func() { _sql, _ := _database.Postgres.DB(); _sql.Close() }() - - // create expected return in mock - _rows := sqlmock.NewRows([]string{"count"}).AddRow(2) - - // ensure the mock expects the query - _mock.ExpectQuery("SELECT count(*) FROM \"builds\" JOIN repos ON builds.repo_id = repos.id and repos.org = $1 WHERE \"event\" = $2").WillReturnRows(_rows) - - // setup tests - tests := []struct { - failure bool - want int64 - }{ - { - failure: false, - want: 2, - }, - } - filters := map[string]interface{}{ - "event": "push", - } - - // run tests - for _, test := range tests { - got, err := _database.GetOrgBuildCount("foo", filters) - - if test.failure { - if err == nil { - t.Errorf("GetOrgBuildCountByEvent should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("GetOrgBuildCountByEvent returned err: %v", err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetOrgBuildCountByEvent is %v, want %v", got, test.want) - } - } -} - -func TestPostgres_Client_GetRepoBuildCount(t *testing.T) { - // setup types - _buildOne := testBuild() - _buildOne.SetID(1) - _buildOne.SetRepoID(1) - _buildOne.SetNumber(1) - _buildOne.SetDeployPayload(nil) - - _buildTwo := testBuild() - _buildTwo.SetID(2) - _buildTwo.SetRepoID(1) - _buildTwo.SetNumber(2) - _buildTwo.SetDeployPayload(nil) - - _repo := testRepo() - _repo.SetID(1) - _repo.SetUserID(1) - _repo.SetHash("baz") - _repo.SetOrg("foo") - _repo.SetName("bar") - _repo.SetFullName("foo/bar") - _repo.SetVisibility("public") - - // setup the test database client - _database, _mock, err := NewTest() - if err != nil { - t.Errorf("unable to create new postgres test database: %v", err) - } - - defer func() { _sql, _ := _database.Postgres.DB(); _sql.Close() }() - - // create expected return in mock - _rows := sqlmock.NewRows([]string{"count"}).AddRow(2) - - // ensure the mock expects the query - _mock.ExpectQuery(`SELECT count(*) FROM "builds" WHERE repo_id = $1`).WillReturnRows(_rows) - - // setup tests - tests := []struct { - failure bool - want int64 - }{ - { - failure: false, - want: 2, - }, - } - - filters := map[string]interface{}{} - - // run tests - for _, test := range tests { - got, err := _database.GetRepoBuildCount(_repo, filters) - - if test.failure { - if err == nil { - t.Errorf("GetRepoBuildCount should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("GetRepoBuildCount returned err: %v", err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetRepoBuildCount is %v, want %v", got, test.want) - } - } -} diff --git a/database/postgres/build_list.go b/database/postgres/build_list.go deleted file mode 100644 index 76fe1dce4..000000000 --- a/database/postgres/build_list.go +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright (c) 2022 Target Brands, Inc. All rights reserved. -// -// Use of this source code is governed by the LICENSE file in this repository. - -package postgres - -import ( - "github.com/go-vela/server/database/postgres/dml" - "github.com/go-vela/types/constants" - "github.com/go-vela/types/database" - "github.com/go-vela/types/library" - "github.com/sirupsen/logrus" -) - -// GetBuildList gets a list of all builds from the database. -func (c *client) GetBuildList() ([]*library.Build, error) { - c.Logger.Trace("listing builds from the database") - - // variable to store query results - b := new([]database.Build) - - // send query to the database and store result in variable - err := c.Postgres. - Table(constants.TableBuild). - Raw(dml.ListBuilds). - Scan(b).Error - - // variable we want to return - builds := []*library.Build{} - // iterate through all query results - for _, build := range *b { - // https://golang.org/doc/faq#closures_and_goroutines - tmp := build - - // convert query result to library type - builds = append(builds, tmp.ToLibrary()) - } - - return builds, err -} - -// GetDeploymentBuildList gets a list of all builds from the database. -func (c *client) GetDeploymentBuildList(deployment string) ([]*library.Build, error) { - c.Logger.WithFields(logrus.Fields{ - "deployment": deployment, - }).Tracef("listing builds for deployment %s from the database", deployment) - - // variable to store query results - b := new([]database.Build) - - filters := map[string]string{} - if len(deployment) > 0 { - filters["source"] = deployment - } - // send query to the database and store result in variable - err := c.Postgres. - Table(constants.TableBuild). - Select("*"). - Where(filters). - Limit(3). - Order("number DESC"). - Scan(b).Error - - // variable we want to return - builds := []*library.Build{} - // iterate through all query results - for _, build := range *b { - // https://golang.org/doc/faq#closures_and_goroutines - tmp := build - - // convert query result to library type - builds = append(builds, tmp.ToLibrary()) - } - - return builds, err -} - -// GetOrgBuildList gets a list of all builds by org name and allows filters from the database. -func (c *client) GetOrgBuildList(org string, filters map[string]interface{}, page, perPage int) ([]*library.Build, int64, error) { - c.Logger.WithFields(logrus.Fields{ - "org": org, - }).Tracef("listing builds for org %s from the database", org) - - // variables to store query results - b := new([]database.Build) - builds := []*library.Build{} - count := int64(0) - - // count the results - count, err := c.GetOrgBuildCount(org, filters) - if err != nil { - return builds, 0, err - } - - // short-circuit if there are no results - if count == 0 { - return builds, 0, nil - } - - // calculate offset for pagination through results - offset := perPage * (page - 1) - - // send query to the database and store result in variable - err = c.Postgres. - Table(constants.TableBuild). - Select("builds.*"). - Joins("JOIN repos ON builds.repo_id = repos.id and repos.org = ?", org). - Where(filters). - Order("created DESC"). - Order("id"). - Limit(perPage). - Offset(offset). - Scan(b).Error - - // iterate through all query results - for _, build := range *b { - // https://golang.org/doc/faq#closures_and_goroutines - tmp := build - - // convert query result to library type - builds = append(builds, tmp.ToLibrary()) - } - - return builds, count, err -} - -// GetRepoBuildList gets a list of all builds by repo ID from the database. -func (c *client) GetRepoBuildList(r *library.Repo, filters map[string]interface{}, before, after int64, page, perPage int) ([]*library.Build, int64, error) { - c.Logger.WithFields(logrus.Fields{ - "org": r.GetOrg(), - "repo": r.GetName(), - }).Tracef("listing builds for repo %s from the database", r.GetFullName()) - - // variable to store query results - b := new([]database.Build) - builds := []*library.Build{} - count := int64(0) - - // count the results - count, err := c.GetRepoBuildCount(r, filters) - if err != nil { - return builds, 0, err - } - - // short-circuit if there are no results - if count == 0 { - return builds, 0, nil - } - - // calculate offset for pagination through results - offset := perPage * (page - 1) - - err = c.Postgres. - Table(constants.TableBuild). - Where("repo_id = ?", r.GetID()). - Where("created < ?", before). - Where("created > ?", after). - Where(filters). - Order("number DESC"). - Limit(perPage). - Offset(offset). - Scan(b).Error - - // iterate through all query results - for _, build := range *b { - // https://golang.org/doc/faq#closures_and_goroutines - tmp := build - - // convert query result to library type - builds = append(builds, tmp.ToLibrary()) - } - - return builds, count, err -} diff --git a/database/postgres/build_list_test.go b/database/postgres/build_list_test.go deleted file mode 100644 index bd2e34878..000000000 --- a/database/postgres/build_list_test.go +++ /dev/null @@ -1,459 +0,0 @@ -// Copyright (c) 2022 Target Brands, Inc. All rights reserved. -// -// Use of this source code is governed by the LICENSE file in this repository. - -package postgres - -import ( - "reflect" - "testing" - "time" - - "github.com/DATA-DOG/go-sqlmock" - - "github.com/go-vela/server/database/postgres/dml" - "github.com/go-vela/types/library" - - "gorm.io/gorm" -) - -func TestPostgres_Client_GetBuildList(t *testing.T) { - // setup types - _buildOne := testBuild() - _buildOne.SetID(1) - _buildOne.SetRepoID(1) - _buildOne.SetNumber(1) - _buildOne.SetDeployPayload(nil) - - _buildTwo := testBuild() - _buildTwo.SetID(2) - _buildTwo.SetRepoID(1) - _buildTwo.SetNumber(2) - _buildTwo.SetDeployPayload(nil) - - // setup the test database client - _database, _mock, err := NewTest() - if err != nil { - t.Errorf("unable to create new postgres test database: %v", err) - } - - defer func() { _sql, _ := _database.Postgres.DB(); _sql.Close() }() - - // capture the current expected SQL query - // - // https://gorm.io/docs/sql_builder.html#DryRun-Mode - _query := _database.Postgres.Session(&gorm.Session{DryRun: true}).Raw(dml.ListBuilds).Statement - - // create expected return in mock - _rows := sqlmock.NewRows( - []string{"id", "repo_id", "pipeline_id", "number", "parent", "event", "event_action", "status", "error", "enqueued", "created", "started", "finished", "deploy", "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, "", nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0). - AddRow(2, 1, nil, 2, 0, "", "", "", "", 0, 0, 0, 0, "", nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0) - - // ensure the mock expects the query - _mock.ExpectQuery(_query.SQL.String()).WillReturnRows(_rows) - - // setup tests - tests := []struct { - failure bool - want []*library.Build - }{ - { - failure: false, - want: []*library.Build{_buildOne, _buildTwo}, - }, - } - - // run tests - for _, test := range tests { - got, err := _database.GetBuildList() - - if test.failure { - if err == nil { - t.Errorf("GetBuildList should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("GetBuildList returned err: %v", err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetBuildList is %v, want %v", got, test.want) - } - } -} - -func TestPostgres_Client_GetDeploymentBuildList(t *testing.T) { - // setup types - _buildOne := testBuild() - _buildOne.SetID(1) - _buildOne.SetRepoID(1) - _buildOne.SetNumber(1) - _buildOne.SetDeployPayload(nil) - _buildOne.SetSource("https://github.com/github/octocat/deployments/1") - - _buildTwo := testBuild() - _buildTwo.SetID(2) - _buildTwo.SetRepoID(1) - _buildTwo.SetNumber(2) - _buildTwo.SetDeployPayload(nil) - _buildTwo.SetSource("https://github.com/github/octocat/deployments/1") - - // setup the test database client - _database, _mock, err := NewTest() - if err != nil { - t.Errorf("unable to create new postgres test database: %v", err) - } - - defer func() { _sql, _ := _database.Postgres.DB(); _sql.Close() }() - - // create expected return in mock - _rows := sqlmock.NewRows( - []string{"id", "repo_id", "pipeline_id", "number", "parent", "event", "event_action", "status", "error", "enqueued", "created", "started", "finished", "deploy", "deploy_payload", "clone", "source", "title", "message", "commit", "sender", "author", "email", "link", "branch", "ref", "base_ref", "head_ref", "host", "runtime", "distribution", "timestamp"}, - ).AddRow(2, 1, nil, 2, 0, "", "", "", "", 0, 0, 0, 0, "", nil, "", "https://github.com/github/octocat/deployments/1", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0). - AddRow(1, 1, nil, 1, 0, "", "", "", "", 0, 0, 0, 0, "", nil, "", "https://github.com/github/octocat/deployments/1", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0) - - // ensure the mock expects the query - _mock.ExpectQuery("SELECT * FROM \"builds\" WHERE \"source\" = $1 ORDER BY number DESC LIMIT 3").WillReturnRows(_rows) - - // setup tests - tests := []struct { - failure bool - want []*library.Build - }{ - { - failure: false, - want: []*library.Build{_buildTwo, _buildOne}, - }, - } - - // run tests - for _, test := range tests { - got, err := _database.GetDeploymentBuildList("https://github.com/github/octocat/deployments/1") - - if test.failure { - if err == nil { - t.Errorf("GetDeploymentBuildList should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("GetDeploymentBuildList returned err: %v", err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetDeploymentBuildList is %v, want %v", got, test.want) - } - } -} - -func TestPostgres_Client_GetOrgBuildList(t *testing.T) { - // setup types - _buildOne := testBuild() - _buildOne.SetID(1) - _buildOne.SetRepoID(1) - _buildOne.SetNumber(1) - _buildOne.SetDeployPayload(nil) - - _buildTwo := testBuild() - _buildTwo.SetID(2) - _buildTwo.SetRepoID(1) - _buildTwo.SetNumber(2) - _buildTwo.SetDeployPayload(nil) - - // setup the test database client - _database, _mock, err := NewTest() - if err != nil { - t.Errorf("unable to create new postgres test database: %v", err) - } - - defer func() { _sql, _ := _database.Postgres.DB(); _sql.Close() }() - - // create expected return in mock - _rows := sqlmock.NewRows([]string{"count"}).AddRow(2) - - // ensure the mock expects the query - _mock.ExpectQuery("SELECT count(*) FROM \"builds\" JOIN repos ON builds.repo_id = repos.id and repos.org = $1").WillReturnRows(_rows) - - // create expected return in mock - _rows = sqlmock.NewRows( - []string{"id", "repo_id", "pipeline_id", "number", "parent", "event", "event_action", "status", "error", "enqueued", "created", "started", "finished", "deploy", "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, "", nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0). - AddRow(2, 1, nil, 2, 0, "", "", "", "", 0, 0, 0, 0, "", nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0) - - // ensure the mock expects the query - _mock.ExpectQuery("SELECT builds.* FROM \"builds\" JOIN repos ON builds.repo_id = repos.id and repos.org = $1 ORDER BY created DESC,id LIMIT 10").WillReturnRows(_rows) - - // setup tests - tests := []struct { - failure bool - want []*library.Build - }{ - { - failure: false, - want: []*library.Build{_buildOne, _buildTwo}, - }, - } - - filters := map[string]interface{}{} - - // run tests - for _, test := range tests { - got, _, err := _database.GetOrgBuildList("foo", filters, 1, 10) - - if test.failure { - if err == nil { - t.Errorf("GetOrgBuildList should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("GetOrgBuildList returned err: %v", err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetOrgBuildList is %v, want %v", got, test.want) - } - } -} - -func TestPostgres_Client_GetOrgBuildList_NonAdmin(t *testing.T) { - // setup types - _buildOne := testBuild() - _buildOne.SetID(1) - _buildOne.SetRepoID(1) - _buildOne.SetNumber(1) - _buildOne.SetDeployPayload(nil) - - _buildTwo := testBuild() - _buildTwo.SetID(2) - _buildTwo.SetRepoID(1) - _buildTwo.SetNumber(2) - _buildTwo.SetDeployPayload(nil) - - // setup the test database client - _database, _mock, err := NewTest() - if err != nil { - t.Errorf("unable to create new postgres test database: %v", err) - } - - defer func() { _sql, _ := _database.Postgres.DB(); _sql.Close() }() - - // create expected return in mock - _rows := sqlmock.NewRows([]string{"count"}).AddRow(2) - - // ensure the mock expects the query - _mock.ExpectQuery("SELECT count(*) FROM \"builds\" JOIN repos ON builds.repo_id = repos.id and repos.org = $1 WHERE \"visibility\" = $2").WillReturnRows(_rows) - - // create expected return in mock - _rows = sqlmock.NewRows( - []string{"id", "repo_id", "pipeline_id", "number", "parent", "event", "event_action", "status", "error", "enqueued", "created", "started", "finished", "deploy", "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, "", nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0) - - // ensure the mock expects the query - _mock.ExpectQuery("SELECT builds.* FROM \"builds\" JOIN repos ON builds.repo_id = repos.id and repos.org = $1 WHERE \"visibility\" = $2 ORDER BY created DESC,id LIMIT 10").WillReturnRows(_rows) - - // setup tests - tests := []struct { - failure bool - want []*library.Build - }{ - { - failure: false, - want: []*library.Build{_buildOne}, - }, - } - - filters := map[string]interface{}{ - "visibility": "public", - } - - // run tests - for _, test := range tests { - got, _, err := _database.GetOrgBuildList("foo", filters, 1, 10) - - if test.failure { - if err == nil { - t.Errorf("GetOrgBuildList should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("GetOrgBuildList returned err: %v", err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetOrgBuildList is %v, want %v", got, test.want) - } - } -} - -func TestPostgres_Client_GetOrgBuildListByEvent(t *testing.T) { - // setup types - _buildOne := testBuild() - _buildOne.SetID(1) - _buildOne.SetRepoID(1) - _buildOne.SetNumber(1) - _buildOne.SetDeployPayload(nil) - - _buildTwo := testBuild() - _buildTwo.SetID(2) - _buildTwo.SetRepoID(1) - _buildTwo.SetNumber(2) - _buildTwo.SetDeployPayload(nil) - - // setup the test database client - _database, _mock, err := NewTest() - if err != nil { - t.Errorf("unable to create new postgres test database: %v", err) - } - - defer func() { _sql, _ := _database.Postgres.DB(); _sql.Close() }() - - // create expected return in mock - _rows := sqlmock.NewRows([]string{"count"}).AddRow(2) - - // ensure the mock expects the query - _mock.ExpectQuery("SELECT count(*) FROM \"builds\" JOIN repos ON builds.repo_id = repos.id and repos.org = $1 WHERE \"event\" = $2").WillReturnRows(_rows) - - // create expected return in mock - _rows = sqlmock.NewRows( - []string{"id", "repo_id", "pipeline_id", "number", "parent", "event", "event_action", "status", "error", "enqueued", "created", "started", "finished", "deploy", "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, "", nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0). - AddRow(2, 1, nil, 2, 0, "", "", "", "", 0, 0, 0, 0, "", nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0) - - // ensure the mock expects the query - _mock.ExpectQuery("SELECT builds.* FROM \"builds\" JOIN repos ON builds.repo_id = repos.id and repos.org = $1 WHERE \"event\" = $2 ORDER BY created DESC,id LIMIT 10").WillReturnRows(_rows) - - // setup tests - tests := []struct { - failure bool - want []*library.Build - }{ - { - failure: false, - want: []*library.Build{_buildOne, _buildTwo}, - }, - } - - filters := map[string]interface{}{ - "event": "push", - } - - // run tests - for _, test := range tests { - got, _, err := _database.GetOrgBuildList("foo", filters, 1, 10) - - if test.failure { - if err == nil { - t.Errorf("GetOrgBuildListByEvent should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("GetOrgBuildListByEvent returned err: %v", err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetOrgBuildListByEvent is %v, want %v", got, test.want) - } - } -} - -func TestPostgres_Client_GetRepoBuildList(t *testing.T) { - // setup types - _buildOne := testBuild() - _buildOne.SetID(1) - _buildOne.SetRepoID(1) - _buildOne.SetNumber(1) - _buildOne.SetDeployPayload(nil) - _buildOne.SetCreated(1) - - _buildTwo := testBuild() - _buildTwo.SetID(2) - _buildTwo.SetRepoID(1) - _buildTwo.SetNumber(2) - _buildTwo.SetDeployPayload(nil) - _buildTwo.SetCreated(2) - - _repo := testRepo() - _repo.SetID(1) - _repo.SetUserID(1) - _repo.SetHash("baz") - _repo.SetOrg("foo") - _repo.SetName("bar") - _repo.SetFullName("foo/bar") - _repo.SetVisibility("public") - - // setup the test database client - _database, _mock, err := NewTest() - if err != nil { - t.Errorf("unable to create new postgres test database: %v", err) - } - - defer func() { _sql, _ := _database.Postgres.DB(); _sql.Close() }() - - // create expected return in mock - _rows := sqlmock.NewRows([]string{"count"}).AddRow(3) - - // ensure the mock expects the query - _mock.ExpectQuery(`SELECT count(*) FROM "builds" WHERE repo_id = $1`).WillReturnRows(_rows) - - // create expected return in mock - _rows = sqlmock.NewRows( - []string{"id", "repo_id", "pipeline_id", "number", "parent", "event", "event_action", "status", "error", "enqueued", "created", "started", "finished", "deploy", "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, 1, 0, 0, "", nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0). - AddRow(2, 1, nil, 2, 0, "", "", "", "", 0, 2, 0, 0, "", nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0) - - // ensure the mock expects the query - _mock.ExpectQuery(`SELECT * FROM "builds" WHERE repo_id = $1 AND created < $2 AND created > $3 ORDER BY number DESC LIMIT 10`).WillReturnRows(_rows) - - // setup tests - tests := []struct { - failure bool - before int64 - after int64 - want []*library.Build - }{ - { - failure: false, - before: time.Now().UTC().Unix(), - after: 0, - want: []*library.Build{_buildOne, _buildTwo}, - }, - } - - filters := map[string]interface{}{} - - // run tests - for _, test := range tests { - got, _, err := _database.GetRepoBuildList(_repo, filters, test.before, test.after, 1, 10) - - if test.failure { - if err == nil { - t.Errorf("GetRepoBuildList should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("GetRepoBuildList returned err: %v", err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetRepoBuildList is %v, want %v", got, test.want) - } - } -} diff --git a/database/postgres/build_test.go b/database/postgres/build_test.go deleted file mode 100644 index 06d6238b2..000000000 --- a/database/postgres/build_test.go +++ /dev/null @@ -1,591 +0,0 @@ -// Copyright (c) 2022 Target Brands, Inc. All rights reserved. -// -// Use of this source code is governed by the LICENSE file in this repository. - -package postgres - -import ( - "reflect" - "testing" - - "github.com/DATA-DOG/go-sqlmock" - - "github.com/go-vela/server/database/postgres/dml" - "github.com/go-vela/types/library" - - "gorm.io/gorm" -) - -func TestPostgres_Client_GetBuild(t *testing.T) { - // setup types - _build := testBuild() - _build.SetID(1) - _build.SetRepoID(1) - _build.SetNumber(1) - _build.SetDeployPayload(nil) - - _repo := testRepo() - _repo.SetID(1) - _repo.SetUserID(1) - _repo.SetHash("baz") - _repo.SetOrg("foo") - _repo.SetName("bar") - _repo.SetFullName("foo/bar") - _repo.SetVisibility("public") - - // setup the test database client - _database, _mock, err := NewTest() - if err != nil { - t.Errorf("unable to create new postgres test database: %v", err) - } - - defer func() { _sql, _ := _database.Postgres.DB(); _sql.Close() }() - - // capture the current expected SQL query - // - // https://gorm.io/docs/sql_builder.html#DryRun-Mode - _query := _database.Postgres.Session(&gorm.Session{DryRun: true}).Raw(dml.SelectRepoBuild, 1, 1).Statement - - // create expected return in mock - _rows := sqlmock.NewRows( - []string{"id", "repo_id", "pipeline_id", "number", "parent", "event", "event_action", "status", "error", "enqueued", "created", "started", "finished", "deploy", "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, "", nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0) - - // ensure the mock expects the query for test case 1 - _mock.ExpectQuery(_query.SQL.String()).WillReturnRows(_rows) - // ensure the mock expects the error for test case 2 - _mock.ExpectQuery(_query.SQL.String()).WillReturnError(gorm.ErrRecordNotFound) - - // setup tests - tests := []struct { - failure bool - want *library.Build - }{ - { - failure: false, - want: _build, - }, - { - failure: true, - want: nil, - }, - } - - // run tests - for _, test := range tests { - got, err := _database.GetBuild(1, _repo) - - if test.failure { - if err == nil { - t.Errorf("GetBuild should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("GetBuild returned err: %v", err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetBuild is %v, want %v", got, test.want) - } - } -} - -func TestPostgres_Client_GetBuildByID(t *testing.T) { - // setup types - _build := testBuild() - _build.SetID(1) - _build.SetRepoID(1) - _build.SetNumber(1) - _build.SetDeployPayload(nil) - - // setup the test database client - _database, _mock, err := NewTest() - if err != nil { - t.Errorf("unable to create new postgres test database: %v", err) - } - - defer func() { _sql, _ := _database.Postgres.DB(); _sql.Close() }() - - // capture the current expected SQL query - // - // https://gorm.io/docs/sql_builder.html#DryRun-Mode - _query := _database.Postgres.Session(&gorm.Session{DryRun: true}).Raw(dml.SelectBuildByID, 1).Statement - - // create expected return in mock - _rows := sqlmock.NewRows( - []string{"id", "repo_id", "pipeline_id", "number", "parent", "event", "event_action", "status", "error", "enqueued", "created", "started", "finished", "deploy", "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, "", nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0) - - // ensure the mock expects the query for test case 1 - _mock.ExpectQuery(_query.SQL.String()).WillReturnRows(_rows) - // ensure the mock expects the error for test case 2 - _mock.ExpectQuery(_query.SQL.String()).WillReturnError(gorm.ErrRecordNotFound) - - // setup tests - tests := []struct { - failure bool - want *library.Build - }{ - { - failure: false, - want: _build, - }, - { - failure: true, - want: nil, - }, - } - - // run tests - for _, test := range tests { - got, err := _database.GetBuildByID(1) - - if test.failure { - if err == nil { - t.Errorf("GetBuildByID should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("GetBuildByID returned err: %v", err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetBuildByID is %v, want %v", got, test.want) - } - } -} - -func TestPostgres_Client_GetLastBuild(t *testing.T) { - // setup types - _build := testBuild() - _build.SetID(1) - _build.SetRepoID(1) - _build.SetNumber(1) - _build.SetDeployPayload(nil) - - _repo := testRepo() - _repo.SetID(1) - _repo.SetUserID(1) - _repo.SetHash("baz") - _repo.SetOrg("foo") - _repo.SetName("bar") - _repo.SetFullName("foo/bar") - _repo.SetVisibility("public") - - // setup the test database client - _database, _mock, err := NewTest() - if err != nil { - t.Errorf("unable to create new postgres test database: %v", err) - } - - defer func() { _sql, _ := _database.Postgres.DB(); _sql.Close() }() - - // capture the current expected SQL query - // - // https://gorm.io/docs/sql_builder.html#DryRun-Mode - _query := _database.Postgres.Session(&gorm.Session{DryRun: true}).Raw(dml.SelectLastRepoBuild, 1).Statement - - // create expected return in mock - _rows := sqlmock.NewRows( - []string{"id", "repo_id", "pipeline_id", "number", "parent", "event", "event_action", "status", "error", "enqueued", "created", "started", "finished", "deploy", "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, "", nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0) - - // ensure the mock expects the query for test case 1 - _mock.ExpectQuery(_query.SQL.String()).WillReturnRows(_rows) - // ensure the mock expects the error for test case 2 - _mock.ExpectQuery(_query.SQL.String()).WillReturnError(gorm.ErrRecordNotFound) - - // setup tests - tests := []struct { - failure bool - want *library.Build - }{ - { - failure: false, - want: _build, - }, - { - failure: false, - want: nil, - }, - } - - // run tests - for _, test := range tests { - got, err := _database.GetLastBuild(_repo) - - if test.failure { - if err == nil { - t.Errorf("GetLastBuild should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("GetLastBuild returned err: %v", err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetLastBuild is %v, want %v", got, test.want) - } - } -} - -func TestPostgres_Client_GetLastBuildByBranch(t *testing.T) { - // setup types - _build := testBuild() - _build.SetID(1) - _build.SetRepoID(1) - _build.SetNumber(1) - _build.SetDeployPayload(nil) - - _repo := testRepo() - _repo.SetID(1) - _repo.SetUserID(1) - _repo.SetHash("baz") - _repo.SetOrg("foo") - _repo.SetName("bar") - _repo.SetFullName("foo/bar") - _repo.SetVisibility("public") - - // setup the test database client - _database, _mock, err := NewTest() - if err != nil { - t.Errorf("unable to create new postgres test database: %v", err) - } - - defer func() { _sql, _ := _database.Postgres.DB(); _sql.Close() }() - - // capture the current expected SQL query - // - // https://gorm.io/docs/sql_builder.html#DryRun-Mode - _query := _database.Postgres.Session(&gorm.Session{DryRun: true}).Raw(dml.SelectLastRepoBuildByBranch, 1, "master").Statement - - // create expected return in mock - _rows := sqlmock.NewRows( - []string{"id", "repo_id", "pipeline_id", "number", "parent", "event", "event_action", "status", "error", "enqueued", "created", "started", "finished", "deploy", "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, "", nil, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 0) - - // ensure the mock expects the query for test case 1 - _mock.ExpectQuery(_query.SQL.String()).WillReturnRows(_rows) - // ensure the mock expects the error for test case 2 - _mock.ExpectQuery(_query.SQL.String()).WillReturnError(gorm.ErrRecordNotFound) - - // setup tests - tests := []struct { - failure bool - want *library.Build - }{ - { - failure: false, - want: _build, - }, - { - failure: false, - want: nil, - }, - } - - // run tests - for _, test := range tests { - got, err := _database.GetLastBuildByBranch(_repo, "master") - - if test.failure { - if err == nil { - t.Errorf("GetLastBuildByBranch should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("GetLastBuildByBranch returned err: %v", err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetLastBuildByBranch is %v, want %v", got, test.want) - } - } -} - -func TestPostgres_Client_GetPendingAndRunningBuilds(t *testing.T) { - // setup types - _buildOne := new(library.BuildQueue) - _buildOne.SetCreated(0) - _buildOne.SetFullName("") - _buildOne.SetNumber(1) - _buildOne.SetStatus("") - - _buildTwo := new(library.BuildQueue) - _buildTwo.SetCreated(0) - _buildTwo.SetFullName("") - _buildTwo.SetNumber(2) - _buildTwo.SetStatus("") - - // setup the test database client - _database, _mock, err := NewTest() - if err != nil { - t.Errorf("unable to create new postgres test database: %v", err) - } - - defer func() { _sql, _ := _database.Postgres.DB(); _sql.Close() }() - - // capture the current expected SQL query - // - // https://gorm.io/docs/sql_builder.html#DryRun-Mode - _query := _database.Postgres.Session(&gorm.Session{DryRun: true}).Raw(dml.SelectPendingAndRunningBuilds, "").Statement - - // create expected return in mock - _rows := sqlmock.NewRows([]string{"created", "full_name", "number", "status"}). - AddRow(0, "", 1, "").AddRow(0, "", 2, "") - - // ensure the mock expects the query for test case 1 - _mock.ExpectQuery(_query.SQL.String()).WillReturnRows(_rows) - // ensure the mock expects the error for test case 2 - _mock.ExpectQuery(_query.SQL.String()).WillReturnError(gorm.ErrRecordNotFound) - - // setup tests - tests := []struct { - failure bool - want []*library.BuildQueue - }{ - { - failure: false, - want: []*library.BuildQueue{_buildOne, _buildTwo}, - }, - { - failure: true, - want: nil, - }, - } - - // run tests - for _, test := range tests { - got, err := _database.GetPendingAndRunningBuilds("") - - if test.failure { - if err == nil { - t.Errorf("GetPendingAndRunningBuilds should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("GetPendingAndRunningBuilds returned err: %v", err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetPendingAndRunningBuilds is %v, want %v", got, test.want) - } - } -} - -func TestPostgres_Client_CreateBuild(t *testing.T) { - // setup types - _build := testBuild() - _build.SetID(1) - _build.SetRepoID(1) - _build.SetNumber(1) - - _repo := testRepo() - _repo.SetID(1) - _repo.SetUserID(1) - _repo.SetHash("baz") - _repo.SetOrg("foo") - _repo.SetName("bar") - _repo.SetFullName("foo/bar") - _repo.SetVisibility("public") - - // setup the test database client - _database, _mock, err := NewTest() - if err != nil { - t.Errorf("unable to create new postgres test database: %v", err) - } - - defer func() { _sql, _ := _database.Postgres.DB(); _sql.Close() }() - - // create expected return in mock - _rows := sqlmock.NewRows([]string{"id"}).AddRow(1) - - // ensure the mock expects the query - _mock.ExpectQuery(`INSERT INTO "builds" ("repo_id","pipeline_id","number","parent","event","event_action","status","error","enqueued","created","started","finished","deploy","deploy_payload","clone","source","title","message","commit","sender","author","email","link","branch","ref","base_ref","head_ref","host","runtime","distribution","id") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$30,$31) RETURNING "id"`). - WithArgs(1, nil, 1, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, AnyArgument{}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 1). - WillReturnRows(_rows) - - // setup tests - tests := []struct { - failure bool - }{ - { - failure: false, - }, - } - - // run tests - for _, test := range tests { - err := _database.CreateBuild(_build) - - if test.failure { - if err == nil { - t.Errorf("CreateBuild should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("CreateBuild returned err: %v", err) - } - } -} - -func TestPostgres_Client_UpdateBuild(t *testing.T) { - // setup types - _build := testBuild() - _build.SetID(1) - _build.SetRepoID(1) - _build.SetNumber(1) - - _repo := testRepo() - _repo.SetID(1) - _repo.SetUserID(1) - _repo.SetHash("baz") - _repo.SetOrg("foo") - _repo.SetName("bar") - _repo.SetFullName("foo/bar") - _repo.SetVisibility("public") - - // setup the test database client - _database, _mock, err := NewTest() - if err != nil { - t.Errorf("unable to create new postgres test database: %v", err) - } - - defer func() { _sql, _ := _database.Postgres.DB(); _sql.Close() }() - - // ensure the mock expects the query - _mock.ExpectExec(`UPDATE "builds" SET "repo_id"=$1,"pipeline_id"=$2,"number"=$3,"parent"=$4,"event"=$5,"event_action"=$6,"status"=$7,"error"=$8,"enqueued"=$9,"created"=$10,"started"=$11,"finished"=$12,"deploy"=$13,"deploy_payload"=$14,"clone"=$15,"source"=$16,"title"=$17,"message"=$18,"commit"=$19,"sender"=$20,"author"=$21,"email"=$22,"link"=$23,"branch"=$24,"ref"=$25,"base_ref"=$26,"head_ref"=$27,"host"=$28,"runtime"=$29,"distribution"=$30 WHERE "id" = $31`). - WithArgs(1, nil, 1, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, AnyArgument{}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 1). - WillReturnResult(sqlmock.NewResult(1, 1)) - - // setup tests - tests := []struct { - failure bool - }{ - { - failure: false, - }, - } - - // run tests - for _, test := range tests { - err := _database.UpdateBuild(_build) - - if test.failure { - if err == nil { - t.Errorf("UpdateBuild should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("UpdateBuild returned err: %v", err) - } - } -} - -func TestPostgres_Client_DeleteBuild(t *testing.T) { - // setup types - // setup the test database client - _database, _mock, err := NewTest() - if err != nil { - t.Errorf("unable to create new postgres test database: %v", err) - } - - defer func() { _sql, _ := _database.Postgres.DB(); _sql.Close() }() - - // capture the current expected SQL query - // - // https://gorm.io/docs/sql_builder.html#DryRun-Mode - _query := _database.Postgres.Session(&gorm.Session{DryRun: true}).Exec(dml.DeleteBuild, 1).Statement - - // ensure the mock expects the query - _mock.ExpectExec(_query.SQL.String()).WillReturnResult(sqlmock.NewResult(1, 1)) - - // setup tests - tests := []struct { - failure bool - }{ - { - failure: false, - }, - } - - // run tests - for _, test := range tests { - err := _database.DeleteBuild(1) - - if test.failure { - if err == nil { - t.Errorf("DeleteBuild should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("DeleteBuild returned err: %v", err) - } - } -} - -// testBuild is a test helper function to create a -// library Build type with all fields set to their -// zero values. -func testBuild() *library.Build { - i64 := int64(0) - i := 0 - str := "" - - return &library.Build{ - ID: &i64, - RepoID: &i64, - PipelineID: &i64, - Number: &i, - Parent: &i, - Event: &str, - EventAction: &str, - Status: &str, - Error: &str, - Enqueued: &i64, - Created: &i64, - Started: &i64, - Finished: &i64, - Deploy: &str, - Clone: &str, - Source: &str, - Title: &str, - Message: &str, - Commit: &str, - Sender: &str, - Author: &str, - Email: &str, - Link: &str, - Branch: &str, - Ref: &str, - BaseRef: &str, - HeadRef: &str, - Host: &str, - Runtime: &str, - Distribution: &str, - } -} diff --git a/database/postgres/ddl/build.go b/database/postgres/ddl/build.go deleted file mode 100644 index 58eacf15b..000000000 --- a/database/postgres/ddl/build.go +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (c) 2022 Target Brands, Inc. All rights reserved. -// -// Use of this source code is governed by the LICENSE file in this repository. - -package ddl - -const ( - // CreateBuildTable represents a query to - // create the builds table for Vela. - CreateBuildTable = ` -CREATE TABLE -IF NOT EXISTS -builds ( - id SERIAL PRIMARY KEY, - repo_id INTEGER, - pipeline_id INTEGER, - number INTEGER, - parent INTEGER, - event VARCHAR(250), - event_action VARCHAR(250), - status VARCHAR(250), - error VARCHAR(1000), - enqueued INTEGER, - created INTEGER, - started INTEGER, - finished INTEGER, - deploy VARCHAR(500), - deploy_payload VARCHAR(2000), - clone VARCHAR(1000), - source VARCHAR(1000), - title VARCHAR(1000), - message VARCHAR(2000), - commit VARCHAR(500), - sender VARCHAR(250), - author VARCHAR(250), - email VARCHAR(500), - link VARCHAR(1000), - branch VARCHAR(500), - ref VARCHAR(500), - base_ref VARCHAR(500), - head_ref VARCHAR(500), - host VARCHAR(250), - runtime VARCHAR(250), - distribution VARCHAR(250), - timestamp INTEGER, - UNIQUE(repo_id, number) -); -` - - // CreateBuildRepoIDIndex represents a query to create an - // index on the builds table for the repo_id column. - CreateBuildRepoIDIndex = ` -CREATE INDEX -IF NOT EXISTS -builds_repo_id -ON builds (repo_id); -` - - // CreateBuildStatusIndex represents a query to create an - // index on the builds table for the status column. - CreateBuildStatusIndex = ` -CREATE INDEX -IF NOT EXISTS -builds_status -ON builds (status); -` - - // CreateBuildCreatedIndex represents a query to create an - // index on the builds table for the created column. - CreateBuildCreatedIndex = ` -CREATE INDEX CONCURRENTLY -IF NOT EXISTS -builds_created -ON builds (created); -` - - // CreateBuildSourceIndex represents a query to create an - // index on the builds table for the source column. - CreateBuildSourceIndex = ` -CREATE INDEX CONCURRENTLY -IF NOT EXISTS -builds_source -ON builds (source); -` -) diff --git a/database/postgres/ddl/doc.go b/database/postgres/ddl/doc.go deleted file mode 100644 index 3e21c882f..000000000 --- a/database/postgres/ddl/doc.go +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) 2022 Target Brands, Inc. All rights reserved. -// -// Use of this source code is governed by the LICENSE file in this repository. - -// Package ddl provides the Postgres data definition language (DDL) for Vela. -// -// https://en.wikipedia.org/wiki/Data_definition_language -// -// Usage: -// -// import "github.com/go-vela/server/database/postgres/ddl" -package ddl diff --git a/database/postgres/dml/build.go b/database/postgres/dml/build.go deleted file mode 100644 index 9080157c6..000000000 --- a/database/postgres/dml/build.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) 2022 Target Brands, Inc. All rights reserved. -// -// Use of this source code is governed by the LICENSE file in this repository. - -package dml - -const ( - // ListBuilds represents a query to - // list all builds in the database. - ListBuilds = ` -SELECT * -FROM builds; -` - - // SelectBuildByID represents a query to select - // a build for its id in the database. - SelectBuildByID = ` -SELECT * -FROM builds -WHERE id = ? -LIMIT 1; -` - - // SelectRepoBuild represents a query to select - // a build for a repo_id in the database. - SelectRepoBuild = ` -SELECT * -FROM builds -WHERE repo_id = ? -AND number = ? -LIMIT 1; -` - - // SelectLastRepoBuild represents a query to select - // the last build for a repo_id in the database. - SelectLastRepoBuild = ` -SELECT * -FROM builds -WHERE repo_id = ? -ORDER BY number DESC -LIMIT 1; -` - - // SelectLastRepoBuildByBranch represents a query to - // select the last build for a repo_id and branch name - // in the database. - SelectLastRepoBuildByBranch = ` -SELECT * -FROM builds -WHERE repo_id = ? -AND branch = ? -ORDER BY number DESC -LIMIT 1; -` - - // SelectBuildsCount represents a query to select - // the count of builds in the database. - SelectBuildsCount = ` -SELECT count(*) as count -FROM builds; -` - - // SelectBuildsCountByStatus represents a query to select - // the count of builds for a status in the database. - SelectBuildsCountByStatus = ` -SELECT count(*) as count -FROM builds -WHERE status = ?; -` - - // DeleteBuild represents a query to - // remove a build from the database. - DeleteBuild = ` -DELETE -FROM builds -WHERE id = ?; -` - - // SelectPendingAndRunningBuilds represents a joined query - // between the builds & repos table to select - // the created builds that are in pending or running builds status - // since the specified timeframe. - SelectPendingAndRunningBuilds = ` -SELECT builds.created, builds.number, builds.status, repos.full_name -FROM builds INNER JOIN repos -ON builds.repo_id = repos.id -WHERE builds.created > ? -AND (builds.status = 'running' OR builds.status = 'pending'); -` -) diff --git a/database/postgres/dml/doc.go b/database/postgres/dml/doc.go deleted file mode 100644 index f7e517d36..000000000 --- a/database/postgres/dml/doc.go +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) 2022 Target Brands, Inc. All rights reserved. -// -// Use of this source code is governed by the LICENSE file in this repository. - -// Package dml provides the Postgres data manipulation language (DML) for Vela. -// -// https://en.wikipedia.org/wiki/Data_manipulation_language -// -// Usage: -// -// import "github.com/go-vela/server/database/postgres/dml" -package dml diff --git a/database/postgres/postgres.go b/database/postgres/postgres.go index 0c316531b..fea05037d 100644 --- a/database/postgres/postgres.go +++ b/database/postgres/postgres.go @@ -5,14 +5,13 @@ package postgres import ( - "fmt" "time" "github.com/DATA-DOG/go-sqlmock" + "github.com/go-vela/server/database/build" "github.com/go-vela/server/database/hook" "github.com/go-vela/server/database/log" "github.com/go-vela/server/database/pipeline" - "github.com/go-vela/server/database/postgres/ddl" "github.com/go-vela/server/database/repo" "github.com/go-vela/server/database/schedule" "github.com/go-vela/server/database/secret" @@ -20,7 +19,6 @@ import ( "github.com/go-vela/server/database/step" "github.com/go-vela/server/database/user" "github.com/go-vela/server/database/worker" - "github.com/go-vela/types/constants" "github.com/sirupsen/logrus" "gorm.io/driver/postgres" @@ -51,6 +49,8 @@ type ( Postgres *gorm.DB // https://pkg.go.dev/github.com/sirupsen/logrus#Entry Logger *logrus.Entry + // https://pkg.go.dev/github.com/go-vela/server/database/build#BuildInterface + build.BuildInterface // https://pkg.go.dev/github.com/go-vela/server/database/hook#HookInterface hook.HookInterface // https://pkg.go.dev/github.com/go-vela/server/database/log#LogInterface @@ -167,6 +167,12 @@ func NewTest() (*client, sqlmock.Sqlmock, error) { return nil, nil, err } + // ensure the mock expects the build queries + _mock.ExpectExec(build.CreatePostgresTable).WillReturnResult(sqlmock.NewResult(1, 1)) + _mock.ExpectExec(build.CreateCreatedIndex).WillReturnResult(sqlmock.NewResult(1, 1)) + _mock.ExpectExec(build.CreateRepoIDIndex).WillReturnResult(sqlmock.NewResult(1, 1)) + _mock.ExpectExec(build.CreateSourceIndex).WillReturnResult(sqlmock.NewResult(1, 1)) + _mock.ExpectExec(build.CreateStatusIndex).WillReturnResult(sqlmock.NewResult(1, 1)) // ensure the mock expects the hook queries _mock.ExpectExec(hook.CreatePostgresTable).WillReturnResult(sqlmock.NewResult(1, 1)) _mock.ExpectExec(hook.CreateRepoIDIndex).WillReturnResult(sqlmock.NewResult(1, 1)) @@ -257,64 +263,6 @@ func setupDatabase(c *client) error { return nil } - // create the tables in the database - err = createTables(c) - if err != nil { - return err - } - - // create the indexes in the database - err = createIndexes(c) - if err != nil { - return err - } - - return nil -} - -// createTables is a helper function to setup -// the database with the necessary tables. -func createTables(c *client) error { - c.Logger.Trace("creating data tables in the postgres database") - - // create the builds table - err := c.Postgres.Exec(ddl.CreateBuildTable).Error - if err != nil { - return fmt.Errorf("unable to create %s table: %w", constants.TableBuild, err) - } - - return nil -} - -// createIndexes is a helper function to setup -// the database with the necessary indexes. -func createIndexes(c *client) error { - c.Logger.Trace("creating data indexes in the postgres database") - - // create the builds_repo_id index for the builds table - err := c.Postgres.Exec(ddl.CreateBuildRepoIDIndex).Error - if err != nil { - return fmt.Errorf("unable to create builds_repo_id index for the %s table: %w", constants.TableBuild, err) - } - - // create the builds_status index for the builds table - err = c.Postgres.Exec(ddl.CreateBuildStatusIndex).Error - if err != nil { - return fmt.Errorf("unable to create builds_status index for the %s table: %w", constants.TableBuild, err) - } - - // create the builds_created index for the builds table - err = c.Postgres.Exec(ddl.CreateBuildCreatedIndex).Error - if err != nil { - return fmt.Errorf("unable to create builds_created index for the %s table: %w", constants.TableBuild, err) - } - - // create the builds_source index for the builds table - err = c.Postgres.Exec(ddl.CreateBuildSourceIndex).Error - if err != nil { - return fmt.Errorf("unable to create builds_source index for the %s table: %w", constants.TableBuild, err) - } - return nil } @@ -322,6 +270,18 @@ func createIndexes(c *client) error { func createServices(c *client) error { var err error + // create the database agnostic engine for builds + // + // https://pkg.go.dev/github.com/go-vela/server/database/build#New + c.BuildInterface, err = build.New( + build.WithClient(c.Postgres), + build.WithLogger(c.Logger), + build.WithSkipCreation(c.config.SkipCreation), + ) + if err != nil { + return err + } + // create the database agnostic engine for hooks // // https://pkg.go.dev/github.com/go-vela/server/database/hook#New diff --git a/database/postgres/postgres_test.go b/database/postgres/postgres_test.go index d61f443fd..a8f74d1ff 100644 --- a/database/postgres/postgres_test.go +++ b/database/postgres/postgres_test.go @@ -5,24 +5,22 @@ package postgres import ( - "database/sql/driver" "testing" "time" "github.com/go-vela/server/database/schedule" "github.com/DATA-DOG/go-sqlmock" + "github.com/go-vela/server/database/build" "github.com/go-vela/server/database/hook" "github.com/go-vela/server/database/log" "github.com/go-vela/server/database/pipeline" - "github.com/go-vela/server/database/postgres/ddl" "github.com/go-vela/server/database/repo" "github.com/go-vela/server/database/secret" "github.com/go-vela/server/database/service" "github.com/go-vela/server/database/step" "github.com/go-vela/server/database/user" "github.com/go-vela/server/database/worker" - "github.com/go-vela/types/library" ) func TestPostgres_New(t *testing.T) { @@ -83,15 +81,12 @@ func TestPostgres_setupDatabase(t *testing.T) { // ensure the mock expects the ping _mock.ExpectPing() - // ensure the mock expects the table queries - _mock.ExpectExec(ddl.CreateBuildTable).WillReturnResult(sqlmock.NewResult(1, 1)) - - // ensure the mock expects the index queries - _mock.ExpectExec(ddl.CreateBuildRepoIDIndex).WillReturnResult(sqlmock.NewResult(1, 1)) - _mock.ExpectExec(ddl.CreateBuildStatusIndex).WillReturnResult(sqlmock.NewResult(1, 1)) - _mock.ExpectExec(ddl.CreateBuildCreatedIndex).WillReturnResult(sqlmock.NewResult(1, 1)) - _mock.ExpectExec(ddl.CreateBuildSourceIndex).WillReturnResult(sqlmock.NewResult(1, 1)) - + // ensure the mock expects the build queries + _mock.ExpectExec(build.CreatePostgresTable).WillReturnResult(sqlmock.NewResult(1, 1)) + _mock.ExpectExec(build.CreateCreatedIndex).WillReturnResult(sqlmock.NewResult(1, 1)) + _mock.ExpectExec(build.CreateRepoIDIndex).WillReturnResult(sqlmock.NewResult(1, 1)) + _mock.ExpectExec(build.CreateSourceIndex).WillReturnResult(sqlmock.NewResult(1, 1)) + _mock.ExpectExec(build.CreateStatusIndex).WillReturnResult(sqlmock.NewResult(1, 1)) // ensure the mock expects the hook queries _mock.ExpectExec(hook.CreatePostgresTable).WillReturnResult(sqlmock.NewResult(1, 1)) _mock.ExpectExec(hook.CreateRepoIDIndex).WillReturnResult(sqlmock.NewResult(1, 1)) @@ -171,87 +166,6 @@ func TestPostgres_setupDatabase(t *testing.T) { } } -func TestPostgres_createTables(t *testing.T) { - // setup types - // setup the test database client - _database, _mock, err := NewTest() - if err != nil { - t.Errorf("unable to create new postgres test database: %v", err) - } - - defer func() { _sql, _ := _database.Postgres.DB(); _sql.Close() }() - - // ensure the mock expects the table queries - _mock.ExpectExec(ddl.CreateBuildTable).WillReturnResult(sqlmock.NewResult(1, 1)) - - tests := []struct { - failure bool - }{ - { - failure: false, - }, - } - - // run tests - for _, test := range tests { - err := createTables(_database) - - if test.failure { - if err == nil { - t.Errorf("createTables should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("createTables returned err: %v", err) - } - } -} - -func TestPostgres_createIndexes(t *testing.T) { - // setup types - // setup the test database client - _database, _mock, err := NewTest() - if err != nil { - t.Errorf("unable to create new postgres test database: %v", err) - } - - defer func() { _sql, _ := _database.Postgres.DB(); _sql.Close() }() - - // ensure the mock expects the index queries - _mock.ExpectExec(ddl.CreateBuildRepoIDIndex).WillReturnResult(sqlmock.NewResult(1, 1)) - _mock.ExpectExec(ddl.CreateBuildStatusIndex).WillReturnResult(sqlmock.NewResult(1, 1)) - _mock.ExpectExec(ddl.CreateBuildCreatedIndex).WillReturnResult(sqlmock.NewResult(1, 1)) - _mock.ExpectExec(ddl.CreateBuildSourceIndex).WillReturnResult(sqlmock.NewResult(1, 1)) - - tests := []struct { - failure bool - }{ - { - failure: false, - }, - } - - // run tests - for _, test := range tests { - err := createIndexes(_database) - - if test.failure { - if err == nil { - t.Errorf("createIndexes should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("createIndexes returned err: %v", err) - } - } -} - func TestPostgres_createServices(t *testing.T) { // setup types // setup the test database client @@ -262,6 +176,12 @@ func TestPostgres_createServices(t *testing.T) { defer func() { _sql, _ := _database.Postgres.DB(); _sql.Close() }() + // ensure the mock expects the build queries + _mock.ExpectExec(build.CreatePostgresTable).WillReturnResult(sqlmock.NewResult(1, 1)) + _mock.ExpectExec(build.CreateCreatedIndex).WillReturnResult(sqlmock.NewResult(1, 1)) + _mock.ExpectExec(build.CreateRepoIDIndex).WillReturnResult(sqlmock.NewResult(1, 1)) + _mock.ExpectExec(build.CreateSourceIndex).WillReturnResult(sqlmock.NewResult(1, 1)) + _mock.ExpectExec(build.CreateStatusIndex).WillReturnResult(sqlmock.NewResult(1, 1)) // ensure the mock expects the hook queries _mock.ExpectExec(hook.CreatePostgresTable).WillReturnResult(sqlmock.NewResult(1, 1)) _mock.ExpectExec(hook.CreateRepoIDIndex).WillReturnResult(sqlmock.NewResult(1, 1)) @@ -318,52 +238,3 @@ func TestPostgres_createServices(t *testing.T) { } } } - -// This will be used with the github.com/DATA-DOG/go-sqlmock -// library to compare values that are otherwise not easily -// compared. These typically would be values generated before -// adding or updating them in the database. -// -// https://github.com/DATA-DOG/go-sqlmock#matching-arguments-like-timetime -type AnyArgument struct{} - -// Match satisfies sqlmock.Argument interface. -func (a AnyArgument) Match(v driver.Value) bool { - return true -} - -// testRepo is a test helper function to create a -// library Repo type with all fields set to their -// zero values. -func testRepo() *library.Repo { - i64 := int64(0) - i := 0 - str := "" - b := false - - return &library.Repo{ - ID: &i64, - PipelineType: &str, - UserID: &i64, - Hash: &str, - Org: &str, - Name: &str, - FullName: &str, - Link: &str, - Clone: &str, - Branch: &str, - BuildLimit: &i64, - Timeout: &i64, - Counter: &i, - Visibility: &str, - Private: &b, - Trusted: &b, - Active: &b, - AllowPull: &b, - AllowPush: &b, - AllowDeploy: &b, - AllowTag: &b, - AllowComment: &b, - PreviousName: &str, - } -} diff --git a/database/sqlite/build.go b/database/sqlite/build.go deleted file mode 100644 index 974f619b0..000000000 --- a/database/sqlite/build.go +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright (c) 2022 Target Brands, Inc. All rights reserved. -// -// Use of this source code is governed by the LICENSE file in this repository. - -package sqlite - -import ( - "errors" - - "github.com/sirupsen/logrus" - - "github.com/go-vela/server/database/sqlite/dml" - "github.com/go-vela/types/constants" - "github.com/go-vela/types/database" - "github.com/go-vela/types/library" - - "gorm.io/gorm" -) - -// GetBuild gets a build by number and repo ID from the database. -func (c *client) GetBuild(number int, r *library.Repo) (*library.Build, error) { - c.Logger.WithFields(logrus.Fields{ - "build": number, - "org": r.GetOrg(), - "repo": r.GetName(), - }).Tracef("getting build %s/%d from the database", r.GetFullName(), number) - - // variable to store query results - b := new(database.Build) - - // send query to the database and store result in variable - result := c.Sqlite. - Table(constants.TableBuild). - Raw(dml.SelectRepoBuild, r.GetID(), number). - Scan(b) - - // check if the query returned a record not found error or no rows were returned - if errors.Is(result.Error, gorm.ErrRecordNotFound) || result.RowsAffected == 0 { - return nil, gorm.ErrRecordNotFound - } - - return b.ToLibrary(), result.Error -} - -// GetBuildByID gets a build by its ID from the database. -func (c *client) GetBuildByID(id int64) (*library.Build, error) { - c.Logger.WithFields(logrus.Fields{ - "build": id, - }).Tracef("getting build %d from the database", id) - - // variable to store query result - b := new(database.Build) - - // send query to the database and store result in variable - result := c.Sqlite. - Table(constants.TableBuild). - Raw(dml.SelectBuildByID, id). - Scan(b) - - // check if the query returned a record not found error or no rows were returned - if errors.Is(result.Error, gorm.ErrRecordNotFound) || result.RowsAffected == 0 { - return nil, gorm.ErrRecordNotFound - } - - return b.ToLibrary(), result.Error -} - -// GetLastBuild gets the last build by repo ID from the database. -func (c *client) GetLastBuild(r *library.Repo) (*library.Build, error) { - c.Logger.WithFields(logrus.Fields{ - "org": r.GetOrg(), - "repo": r.GetName(), - }).Tracef("getting last build for repo %s from the database", r.GetFullName()) - - // variable to store query results - b := new(database.Build) - - // send query to the database and store result in variable - result := c.Sqlite. - Table(constants.TableBuild). - Raw(dml.SelectLastRepoBuild, r.GetID()). - Scan(b) - - // check if the query returned a record not found error or no rows were returned - if errors.Is(result.Error, gorm.ErrRecordNotFound) || result.RowsAffected == 0 { - // the record will not exist if it's a new repo - return nil, nil - } - - return b.ToLibrary(), result.Error -} - -// GetLastBuildByBranch gets the last build by repo ID and branch from the database. -func (c *client) GetLastBuildByBranch(r *library.Repo, branch string) (*library.Build, error) { - c.Logger.WithFields(logrus.Fields{ - "org": r.GetOrg(), - "repo": r.GetName(), - }).Tracef("getting last build for repo %s on branch %s from the database", r.GetFullName(), branch) - - // variable to store query results - b := new(database.Build) - - // send query to the database and store result in variable - result := c.Sqlite. - Table(constants.TableBuild). - Raw(dml.SelectLastRepoBuildByBranch, r.GetID(), branch). - Scan(b) - - // check if the query returned a record not found error or no rows were returned - if errors.Is(result.Error, gorm.ErrRecordNotFound) || result.RowsAffected == 0 { - // the record will not exist if it's a new repo - return nil, nil - } - - return b.ToLibrary(), result.Error -} - -// GetPendingAndRunningBuilds returns the list of pending -// and running builds within the given timeframe. -func (c *client) GetPendingAndRunningBuilds(after string) ([]*library.BuildQueue, error) { - c.Logger.Trace("getting pending and running builds from the database") - - // variable to store query results - b := new([]database.BuildQueue) - - // send query to the database and store result in variable - result := c.Sqlite. - Table(constants.TableBuild). - Raw(dml.SelectPendingAndRunningBuilds, after). - Scan(b) - - // variable we want to return - builds := []*library.BuildQueue{} - - // iterate through all query results - for _, build := range *b { - // https://golang.org/doc/faq#closures_and_goroutines - tmp := build - - // convert query result to library type - builds = append(builds, tmp.ToLibrary()) - } - - return builds, result.Error -} - -// CreateBuild creates a new build in the database. -func (c *client) CreateBuild(b *library.Build) error { - c.Logger.WithFields(logrus.Fields{ - "build": b.GetNumber(), - }).Tracef("creating build %d in the database", b.GetNumber()) - - // cast to database type - build := database.BuildFromLibrary(b) - - // validate the necessary fields are populated - err := build.Validate() - if err != nil { - return err - } - - // send query to the database - return c.Sqlite. - Table(constants.TableBuild). - Create(build.Crop()).Error -} - -// UpdateBuild updates a build in the database. -func (c *client) UpdateBuild(b *library.Build) error { - c.Logger.WithFields(logrus.Fields{ - "build": b.GetNumber(), - }).Tracef("updating build %d in the database", b.GetNumber()) - - // cast to database type - build := database.BuildFromLibrary(b) - - // validate the necessary fields are populated - err := build.Validate() - if err != nil { - return err - } - - // send query to the database - return c.Sqlite. - Table(constants.TableBuild). - Save(build.Crop()).Error -} - -// DeleteBuild deletes a build by unique ID from the database. -func (c *client) DeleteBuild(id int64) error { - c.Logger.Tracef("deleting build %d in the database", id) - - // send query to the database - return c.Sqlite. - Table(constants.TableBuild). - Exec(dml.DeleteBuild, id).Error -} diff --git a/database/sqlite/build_count.go b/database/sqlite/build_count.go deleted file mode 100644 index 2a363ddcf..000000000 --- a/database/sqlite/build_count.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) 2022 Target Brands, Inc. All rights reserved. -// -// Use of this source code is governed by the LICENSE file in this repository. - -package sqlite - -import ( - "github.com/go-vela/server/database/sqlite/dml" - "github.com/go-vela/types/constants" - "github.com/go-vela/types/library" - "github.com/sirupsen/logrus" -) - -// GetBuildCount gets a count of all builds from the database. -func (c *client) GetBuildCount() (int64, error) { - c.Logger.Trace("getting count of builds from the database") - - // variable to store query results - var b int64 - - // send query to the database and store result in variable - err := c.Sqlite. - Table(constants.TableBuild). - Raw(dml.SelectBuildsCount). - Pluck("count", &b).Error - - return b, err -} - -// GetBuildCountByStatus gets a count of all builds by status from the database. -func (c *client) GetBuildCountByStatus(status string) (int64, error) { - c.Logger.Tracef("getting count of builds by status %s from the database", status) - - // variable to store query results - var b int64 - - // send query to the database and store result in variable - err := c.Sqlite. - Table(constants.TableBuild). - Raw(dml.SelectBuildsCountByStatus, status). - Pluck("count", &b).Error - - return b, err -} - -// GetOrgBuildCount gets the count of all builds by repo ID from the database. -func (c *client) GetOrgBuildCount(org string, filters map[string]interface{}) (int64, error) { - c.Logger.WithFields(logrus.Fields{ - "org": org, - }).Tracef("getting count of builds for org %s from the database", org) - - // variable to store query results - var b int64 - - // send query to the database and store result in variable - err := c.Sqlite. - Table(constants.TableBuild). - Joins("JOIN repos ON builds.repo_id = repos.id and repos.org = ?", org). - Where(filters). - Count(&b).Error - - return b, err -} - -// GetRepoBuildCount gets the count of all builds by repo ID from the database. -func (c *client) GetRepoBuildCount(r *library.Repo, filters map[string]interface{}) (int64, error) { - c.Logger.WithFields(logrus.Fields{ - "org": r.GetOrg(), - "name": r.GetName(), - }).Tracef("getting count of builds for repo %s from the database", r.GetFullName()) - - // variable to store query results - var b int64 - - // send query to the database and store result in variable - err := c.Sqlite. - Table(constants.TableBuild). - Where("repo_id = ?", r.GetID()). - Where(filters). - Count(&b).Error - - return b, err -} diff --git a/database/sqlite/build_count_test.go b/database/sqlite/build_count_test.go deleted file mode 100644 index ec4c2120d..000000000 --- a/database/sqlite/build_count_test.go +++ /dev/null @@ -1,439 +0,0 @@ -// Copyright (c) 2022 Target Brands, Inc. All rights reserved. -// -// Use of this source code is governed by the LICENSE file in this repository. - -package sqlite - -import ( - "log" - "reflect" - "testing" - - "github.com/go-vela/server/database/sqlite/ddl" - "github.com/go-vela/types/constants" -) - -func init() { - // setup the test database client - _database, err := NewTest() - if err != nil { - log.Fatalf("unable to create new sqlite test database: %v", err) - } - - // create the build table - err = _database.Sqlite.Exec(ddl.CreateBuildTable).Error - if err != nil { - log.Fatalf("unable to create %s table: %v", constants.TableBuild, err) - } -} - -func TestSqlite_Client_GetBuildCount(t *testing.T) { - // setup types - _buildOne := testBuild() - _buildOne.SetID(1) - _buildOne.SetRepoID(1) - _buildOne.SetNumber(1) - _buildOne.SetDeployPayload(nil) - - _buildTwo := testBuild() - _buildTwo.SetID(2) - _buildTwo.SetRepoID(1) - _buildTwo.SetNumber(2) - _buildTwo.SetDeployPayload(nil) - - // setup the test database client - _database, err := NewTest() - if err != nil { - t.Errorf("unable to create new sqlite test database: %v", err) - } - - defer func() { _sql, _ := _database.Sqlite.DB(); _sql.Close() }() - - // setup tests - tests := []struct { - failure bool - want int64 - }{ - { - failure: false, - want: 2, - }, - } - - // run tests - for _, test := range tests { - // defer cleanup of the builds table - defer _database.Sqlite.Exec("delete from builds;") - - // create the builds in the database - err := _database.CreateBuild(_buildOne) - if err != nil { - t.Errorf("unable to create test build: %v", err) - } - - err = _database.CreateBuild(_buildTwo) - if err != nil { - t.Errorf("unable to create test build: %v", err) - } - - got, err := _database.GetBuildCount() - - if test.failure { - if err == nil { - t.Errorf("GetBuildCount should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("GetBuildCount returned err: %v", err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetBuildCount is %v, want %v", got, test.want) - } - } -} - -func TestSqlite_Client_GetBuildCountByStatus(t *testing.T) { - // setup types - _buildOne := testBuild() - _buildOne.SetID(1) - _buildOne.SetRepoID(1) - _buildOne.SetNumber(1) - _buildOne.SetStatus("running") - _buildOne.SetDeployPayload(nil) - - _buildTwo := testBuild() - _buildTwo.SetID(2) - _buildTwo.SetRepoID(1) - _buildTwo.SetNumber(2) - _buildTwo.SetStatus("running") - _buildTwo.SetDeployPayload(nil) - - // setup the test database client - _database, err := NewTest() - if err != nil { - t.Errorf("unable to create new sqlite test database: %v", err) - } - - defer func() { _sql, _ := _database.Sqlite.DB(); _sql.Close() }() - - // setup tests - tests := []struct { - failure bool - want int64 - }{ - { - failure: false, - want: 2, - }, - } - - // run tests - for _, test := range tests { - // defer cleanup of the builds table - defer _database.Sqlite.Exec("delete from builds;") - - // create the builds in the database - err := _database.CreateBuild(_buildOne) - if err != nil { - t.Errorf("unable to create test build: %v", err) - } - - err = _database.CreateBuild(_buildTwo) - if err != nil { - t.Errorf("unable to create test build: %v", err) - } - - got, err := _database.GetBuildCountByStatus("running") - - if test.failure { - if err == nil { - t.Errorf("GetBuildCountByStatus should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("GetBuildCountByStatus returned err: %v", err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetBuildCountByStatus is %v, want %v", got, test.want) - } - } -} - -func TestSqlite_Client_GetOrgBuildCount(t *testing.T) { - // setup types - _buildOne := testBuild() - _buildOne.SetID(1) - _buildOne.SetRepoID(1) - _buildOne.SetNumber(1) - _buildOne.SetDeployPayload(nil) - - _buildTwo := testBuild() - _buildTwo.SetID(2) - _buildTwo.SetRepoID(1) - _buildTwo.SetNumber(2) - _buildTwo.SetDeployPayload(nil) - - _repo := testRepo() - _repo.SetID(1) - _repo.SetUserID(1) - _repo.SetHash("baz") - _repo.SetOrg("foo") - _repo.SetName("bar") - _repo.SetFullName("foo/bar") - _repo.SetVisibility("public") - - // setup the test database client - _database, err := NewTest() - if err != nil { - t.Errorf("unable to create new sqlite test database: %v", err) - } - - defer func() { _sql, _ := _database.Sqlite.DB(); _sql.Close() }() - - // setup tests - tests := []struct { - failure bool - want int64 - }{ - { - failure: false, - want: 2, - }, - } - - filters := map[string]interface{}{} - - // run tests - for _, test := range tests { - // defer cleanup of the repos table - defer _database.Sqlite.Exec("delete from repos;") - - // create the repo in the database - err := _database.CreateRepo(_repo) - if err != nil { - t.Errorf("unable to create test repo: %v", err) - } - - // defer cleanup of the builds table - defer _database.Sqlite.Exec("delete from builds;") - - // create the builds in the database - err = _database.CreateBuild(_buildOne) - if err != nil { - t.Errorf("unable to create test build: %v", err) - } - - err = _database.CreateBuild(_buildTwo) - if err != nil { - t.Errorf("unable to create test build: %v", err) - } - - got, err := _database.GetOrgBuildCount("foo", filters) - - if test.failure { - if err == nil { - t.Errorf("GetOrgBuildCount should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("GetOrgBuildCount returned err: %v", err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetOrgBuildCount is %v, want %v", got, test.want) - } - } -} - -func TestSqlite_Client_GetOrgBuildCountByEvent(t *testing.T) { - // setup types - _buildOne := testBuild() - _buildOne.SetID(1) - _buildOne.SetRepoID(1) - _buildOne.SetNumber(1) - _buildOne.SetEvent("push") - _buildOne.SetDeployPayload(nil) - - _buildTwo := testBuild() - _buildTwo.SetID(2) - _buildTwo.SetRepoID(1) - _buildTwo.SetNumber(2) - _buildTwo.SetEvent("push") - _buildTwo.SetDeployPayload(nil) - - _repo := testRepo() - _repo.SetID(1) - _repo.SetUserID(1) - _repo.SetHash("baz") - _repo.SetOrg("foo") - _repo.SetName("bar") - _repo.SetFullName("foo/bar") - _repo.SetVisibility("public") - - // setup the test database client - _database, err := NewTest() - if err != nil { - t.Errorf("unable to create new sqlite test database: %v", err) - } - - defer func() { _sql, _ := _database.Sqlite.DB(); _sql.Close() }() - - // setup tests - tests := []struct { - failure bool - want int64 - }{ - { - failure: false, - want: 2, - }, - } - - filters := map[string]interface{}{ - "event": "push", - } - - // run tests - for _, test := range tests { - // defer cleanup of the repos table - defer _database.Sqlite.Exec("delete from repos;") - - // create the repo in the database - err := _database.CreateRepo(_repo) - if err != nil { - t.Errorf("unable to create test repo: %v", err) - } - - // defer cleanup of the builds table - defer _database.Sqlite.Exec("delete from builds;") - - // create the builds in the database - err = _database.CreateBuild(_buildOne) - if err != nil { - t.Errorf("unable to create test build: %v", err) - } - - err = _database.CreateBuild(_buildTwo) - if err != nil { - t.Errorf("unable to create test build: %v", err) - } - - got, err := _database.GetOrgBuildCount("foo", filters) - - if test.failure { - if err == nil { - t.Errorf("GetOrgBuildCountByEvent should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("GetOrgBuildCountByEvent returned err: %v", err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetOrgBuildCountByEvent is %v, want %v", got, test.want) - } - } -} - -func TestSqlite_Client_GetRepoBuildCount(t *testing.T) { - // setup types - _buildOne := testBuild() - _buildOne.SetID(1) - _buildOne.SetRepoID(1) - _buildOne.SetNumber(1) - _buildOne.SetDeployPayload(nil) - - _buildTwo := testBuild() - _buildTwo.SetID(2) - _buildTwo.SetRepoID(1) - _buildTwo.SetNumber(2) - _buildTwo.SetDeployPayload(nil) - - _repo := testRepo() - _repo.SetID(1) - _repo.SetUserID(1) - _repo.SetHash("baz") - _repo.SetOrg("foo") - _repo.SetName("bar") - _repo.SetFullName("foo/bar") - _repo.SetVisibility("public") - - // setup the test database client - _database, err := NewTest() - if err != nil { - t.Errorf("unable to create new sqlite test database: %v", err) - } - - defer func() { _sql, _ := _database.Sqlite.DB(); _sql.Close() }() - - // setup tests - tests := []struct { - failure bool - want int64 - }{ - { - failure: false, - want: 2, - }, - } - - filters := map[string]interface{}{} - - // run tests - for _, test := range tests { - // defer cleanup of the repos table - defer _database.Sqlite.Exec("delete from repos;") - - // create the repo in the database - err := _database.CreateRepo(_repo) - if err != nil { - t.Errorf("unable to create test repo: %v", err) - } - - // defer cleanup of the builds table - defer _database.Sqlite.Exec("delete from builds;") - - // create the builds in the database - err = _database.CreateBuild(_buildOne) - if err != nil { - t.Errorf("unable to create test build: %v", err) - } - - err = _database.CreateBuild(_buildTwo) - if err != nil { - t.Errorf("unable to create test build: %v", err) - } - - got, err := _database.GetRepoBuildCount(_repo, filters) - - if test.failure { - if err == nil { - t.Errorf("GetRepoBuildCount should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("GetRepoBuildCount returned err: %v", err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetRepoBuildCount is %v, want %v", got, test.want) - } - } -} diff --git a/database/sqlite/build_list.go b/database/sqlite/build_list.go deleted file mode 100644 index 300a7c9a8..000000000 --- a/database/sqlite/build_list.go +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright (c) 2022 Target Brands, Inc. All rights reserved. -// -// Use of this source code is governed by the LICENSE file in this repository. - -package sqlite - -import ( - "github.com/go-vela/server/database/sqlite/dml" - "github.com/go-vela/types/constants" - "github.com/go-vela/types/database" - "github.com/go-vela/types/library" - "github.com/sirupsen/logrus" -) - -// GetBuildList gets a list of all builds from the database. -func (c *client) GetBuildList() ([]*library.Build, error) { - c.Logger.Trace("listing builds from the database") - - // variable to store query results - b := new([]database.Build) - - // send query to the database and store result in variable - err := c.Sqlite. - Table(constants.TableBuild). - Raw(dml.ListBuilds). - Scan(b).Error - - // variable we want to return - builds := []*library.Build{} - // iterate through all query results - for _, build := range *b { - // https://golang.org/doc/faq#closures_and_goroutines - tmp := build - - // convert query result to library type - builds = append(builds, tmp.ToLibrary()) - } - - return builds, err -} - -// GetDeploymentBuildList gets a list of all builds from the database. -func (c *client) GetDeploymentBuildList(deployment string) ([]*library.Build, error) { - c.Logger.WithFields(logrus.Fields{ - "deployment": deployment, - }).Tracef("listing builds for deployment %s from the database", deployment) - - // variable to store query results - b := new([]database.Build) - filters := map[string]string{} - - if len(deployment) > 0 { - filters["source"] = deployment - } - // send query to the database and store result in variable - err := c.Sqlite. - Table(constants.TableBuild). - Select("*"). - Where(filters). - Limit(3). - Order("number DESC"). - Scan(b).Error - - // variable we want to return - builds := []*library.Build{} - // iterate through all query results - for _, build := range *b { - // https://golang.org/doc/faq#closures_and_goroutines - tmp := build - - // convert query result to library type - builds = append(builds, tmp.ToLibrary()) - } - - return builds, err -} - -// GetOrgBuildList gets a list of all builds by org name from the database. -func (c *client) GetOrgBuildList(org string, filters map[string]interface{}, page int, perPage int) ([]*library.Build, int64, error) { - c.Logger.WithFields(logrus.Fields{ - "org": org, - }).Tracef("listing builds for org %s from the database", org) - - // variable to store query results - b := new([]database.Build) - builds := []*library.Build{} - count := int64(0) - - // // count the results - count, err := c.GetOrgBuildCount(org, filters) - - if err != nil { - return builds, 0, err - } - - // short-circuit if there are no results - if count == 0 { - return builds, 0, nil - } - - // calculate offset for pagination through results - offset := perPage * (page - 1) - - // send query to the database and store result in variable - err = c.Sqlite. - Table(constants.TableBuild). - Select("builds.*"). - Joins("JOIN repos ON builds.repo_id = repos.id AND repos.org = ?", org). - Where(filters). - Order("created DESC"). - Order("id"). - Limit(perPage). - Offset(offset). - Scan(b).Error - - // iterate through all query results - for _, build := range *b { - // https://golang.org/doc/faq#closures_and_goroutines - tmp := build - - // convert query result to library type - builds = append(builds, tmp.ToLibrary()) - } - - return builds, count, err -} - -// GetRepoBuildList gets a list of all builds by repo ID from the database. -func (c *client) GetRepoBuildList(r *library.Repo, filters map[string]interface{}, before, after int64, page, perPage int) ([]*library.Build, int64, error) { - c.Logger.WithFields(logrus.Fields{ - "org": r.GetOrg(), - "repo": r.GetName(), - }).Tracef("listing builds for repo %s from the database", r.GetFullName()) - - // variable to store query results - b := new([]database.Build) - builds := []*library.Build{} - count := int64(0) - - // count the results - count, err := c.GetRepoBuildCount(r, filters) - if err != nil { - return builds, 0, err - } - - // short-circuit if there are no results - if count == 0 { - return builds, 0, nil - } - - // calculate offset for pagination through results - offset := perPage * (page - 1) - - // send query to the database and store result in variable - err = c.Sqlite. - Table(constants.TableBuild). - Where("repo_id = ?", r.GetID()). - Where("created < ?", before). - Where("created > ?", after). - Where(filters). - Order("number DESC"). - Limit(perPage). - Offset(offset). - Scan(b).Error - - // iterate through all query results - for _, build := range *b { - // https://golang.org/doc/faq#closures_and_goroutines - tmp := build - - // convert query result to library type - builds = append(builds, tmp.ToLibrary()) - } - - return builds, count, err -} diff --git a/database/sqlite/build_list_test.go b/database/sqlite/build_list_test.go deleted file mode 100644 index 908c501bc..000000000 --- a/database/sqlite/build_list_test.go +++ /dev/null @@ -1,532 +0,0 @@ -// Copyright (c) 2022 Target Brands, Inc. All rights reserved. -// -// Use of this source code is governed by the LICENSE file in this repository. - -package sqlite - -import ( - "log" - "reflect" - "testing" - "time" - - "github.com/go-vela/server/database/sqlite/ddl" - "github.com/go-vela/types/constants" - "github.com/go-vela/types/library" -) - -func init() { - // setup the test database client - _database, err := NewTest() - if err != nil { - log.Fatalf("unable to create new sqlite test database: %v", err) - } - - // create the build table - err = _database.Sqlite.Exec(ddl.CreateBuildTable).Error - if err != nil { - log.Fatalf("unable to create %s table: %v", constants.TableBuild, err) - } -} - -func TestSqlite_Client_GetBuildList(t *testing.T) { - // setup types - _buildOne := testBuild() - _buildOne.SetID(1) - _buildOne.SetRepoID(1) - _buildOne.SetNumber(1) - _buildOne.SetDeployPayload(nil) - - _buildTwo := testBuild() - _buildTwo.SetID(2) - _buildTwo.SetRepoID(1) - _buildTwo.SetNumber(2) - _buildTwo.SetDeployPayload(nil) - - // setup the test database client - _database, err := NewTest() - if err != nil { - t.Errorf("unable to create new sqlite test database: %v", err) - } - - defer func() { _sql, _ := _database.Sqlite.DB(); _sql.Close() }() - - // setup tests - tests := []struct { - failure bool - want []*library.Build - }{ - { - failure: false, - want: []*library.Build{_buildOne, _buildTwo}, - }, - } - - // run tests - for _, test := range tests { - // defer cleanup of the builds table - defer _database.Sqlite.Exec("delete from builds;") - - for _, build := range test.want { - // create the build in the database - err := _database.CreateBuild(build) - if err != nil { - t.Errorf("unable to create test build: %v", err) - } - } - - got, err := _database.GetBuildList() - - if test.failure { - if err == nil { - t.Errorf("GetBuildList should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("GetBuildList returned err: %v", err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetBuildList is %v, want %v", got, test.want) - } - } -} - -func TestSqlite_Client_GetDeploymentBuildList(t *testing.T) { - // setup types - _buildOne := testBuild() - _buildOne.SetID(1) - _buildOne.SetRepoID(1) - _buildOne.SetNumber(1) - _buildOne.SetEvent("deployment") - _buildOne.SetDeployPayload(nil) - _buildOne.SetSource("https://github.com/github/octocat/deployments/1") - - _buildTwo := testBuild() - _buildTwo.SetID(2) - _buildTwo.SetRepoID(1) - _buildTwo.SetNumber(2) - _buildOne.SetEvent("deployment") - _buildTwo.SetDeployPayload(nil) - _buildTwo.SetSource("https://github.com/github/octocat/deployments/1") - - // setup the test database client - _database, err := NewTest() - if err != nil { - t.Errorf("unable to create new sqlite test database: %v", err) - } - - defer func() { _sql, _ := _database.Sqlite.DB(); _sql.Close() }() - - // setup tests - tests := []struct { - failure bool - want []*library.Build - }{ - { - failure: false, - want: []*library.Build{_buildTwo, _buildOne}, - }, - } - // run tests - for _, test := range tests { - // defer cleanup of the repos table - defer _database.Sqlite.Exec("delete from repos;") - - // defer cleanup of the builds table - defer _database.Sqlite.Exec("delete from builds;") - - for _, build := range test.want { - // create the build in the database - err := _database.CreateBuild(build) - if err != nil { - t.Errorf("unable to create test build: %v", err) - } - } - - got, err := _database.GetDeploymentBuildList("https://github.com/github/octocat/deployments/1") - - if test.failure { - if err == nil { - t.Errorf("GetDeploymentBuildList should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("GetDeploymentBuildList returned err: %v", err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetDeploymentBuildList is %v, want %v", got, test.want) - } - } -} - -func TestSqlite_Client_GetOrgBuildList(t *testing.T) { - // setup types - _buildOne := testBuild() - _buildOne.SetID(1) - _buildOne.SetRepoID(1) - _buildOne.SetNumber(1) - _buildOne.SetEvent("push") - _buildOne.SetDeployPayload(nil) - - _buildTwo := testBuild() - _buildTwo.SetID(2) - _buildTwo.SetRepoID(1) - _buildTwo.SetNumber(2) - _buildOne.SetEvent("deployment") - _buildTwo.SetDeployPayload(nil) - - _repo := testRepo() - _repo.SetID(1) - _repo.SetUserID(1) - _repo.SetHash("baz") - _repo.SetOrg("foo") - _repo.SetName("bar") - _repo.SetFullName("foo/bar") - _repo.SetVisibility("public") - - // setup the test database client - _database, err := NewTest() - if err != nil { - t.Errorf("unable to create new sqlite test database: %v", err) - } - - defer func() { _sql, _ := _database.Sqlite.DB(); _sql.Close() }() - - // setup tests - tests := []struct { - failure bool - want []*library.Build - }{ - { - failure: false, - want: []*library.Build{_buildOne, _buildTwo}, - }, - } - - filters := map[string]interface{}{} - - // run tests - for _, test := range tests { - // defer cleanup of the repos table - defer _database.Sqlite.Exec("delete from repos;") - - // create the repo in the database - err := _database.CreateRepo(_repo) - if err != nil { - t.Errorf("unable to create test repo: %v", err) - } - - // defer cleanup of the builds table - defer _database.Sqlite.Exec("delete from builds;") - - for _, build := range test.want { - // create the build in the database - err := _database.CreateBuild(build) - if err != nil { - t.Errorf("unable to create test build: %v", err) - } - } - - got, _, err := _database.GetOrgBuildList("foo", filters, 1, 10) - - if test.failure { - if err == nil { - t.Errorf("GetOrgBuildList should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("GetOrgBuildList returned err: %v", err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetOrgBuildList is %v, want %v", got, test.want) - } - } -} - -func TestSqlite_Client_GetOrgBuildList_NonAdmin(t *testing.T) { - // setup types - _buildOne := testBuild() - _buildOne.SetID(1) - _buildOne.SetRepoID(1) - _buildOne.SetNumber(1) - _buildOne.SetDeployPayload(nil) - - _buildTwo := testBuild() - _buildTwo.SetID(2) - _buildTwo.SetRepoID(2) - _buildTwo.SetNumber(2) - _buildTwo.SetDeployPayload(nil) - - _repoOne := testRepo() - _repoOne.SetID(1) - _repoOne.SetUserID(1) - _repoOne.SetHash("baz") - _repoOne.SetOrg("foo") - _repoOne.SetName("bar") - _repoOne.SetFullName("foo/bar") - _repoOne.SetVisibility("public") - - _repoTwo := testRepo() - _repoTwo.SetID(2) - _repoTwo.SetUserID(1) - _repoTwo.SetHash("baz") - _repoTwo.SetOrg("bar") - _repoTwo.SetName("foo") - _repoTwo.SetFullName("bar/foo") - _repoTwo.SetVisibility("private") - - // setup the test database client - _database, err := NewTest() - if err != nil { - t.Errorf("unable to create new sqlite test database: %v", err) - } - - defer func() { _sql, _ := _database.Sqlite.DB(); _sql.Close() }() - - // setup tests - tests := []struct { - failure bool - want []*library.Build - }{ - { - failure: false, - want: []*library.Build{_buildOne}, - }, - } - - filters := map[string]interface{}{} - - repos := []*library.Repo{_repoOne, _repoTwo} - // run tests - for _, test := range tests { - // defer cleanup of the repos table - defer _database.Sqlite.Exec("delete from repos;") - - for _, repo := range repos { - // create the repo in the database - err := _database.CreateRepo(repo) - if err != nil { - t.Errorf("unable to create test repo: %v", err) - } - } - - // defer cleanup of the builds table - defer _database.Sqlite.Exec("delete from builds;") - - for _, build := range test.want { - // create the build in the database - err := _database.CreateBuild(build) - if err != nil { - t.Errorf("unable to create test build: %v", err) - } - } - - got, _, err := _database.GetOrgBuildList("foo", filters, 1, 10) - - if test.failure { - if err == nil { - t.Errorf("GetOrgBuildList should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("GetOrgBuildList returned err: %v", err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetOrgBuildList is %v, want %v", got, test.want) - } - } -} - -func TestSqlite_Client_GetOrgBuildListByEvent(t *testing.T) { - // setup types - _buildOne := testBuild() - _buildOne.SetID(1) - _buildOne.SetRepoID(1) - _buildOne.SetNumber(1) - _buildOne.SetEvent("push") - _buildOne.SetDeployPayload(nil) - - _buildTwo := testBuild() - _buildTwo.SetID(2) - _buildTwo.SetRepoID(1) - _buildTwo.SetNumber(2) - _buildTwo.SetEvent("deployment") - _buildTwo.SetDeployPayload(nil) - - _repo := testRepo() - _repo.SetID(1) - _repo.SetUserID(1) - _repo.SetHash("baz") - _repo.SetOrg("foo") - _repo.SetName("bar") - _repo.SetFullName("foo/bar") - _repo.SetVisibility("public") - - // setup the test database client - _database, err := NewTest() - if err != nil { - t.Errorf("unable to create new sqlite test database: %v", err) - } - - defer func() { _sql, _ := _database.Sqlite.DB(); _sql.Close() }() - - // setup tests - tests := []struct { - failure bool - want []*library.Build - }{ - { - failure: false, - want: []*library.Build{_buildOne}, - }, - } - - filters := map[string]interface{}{ - "event": "push", - } - - // run tests - for _, test := range tests { - // defer cleanup of the repos table - defer _database.Sqlite.Exec("delete from repos;") - - // create the repo in the database - err := _database.CreateRepo(_repo) - if err != nil { - t.Errorf("unable to create test repo: %v", err) - } - - // defer cleanup of the builds table - defer _database.Sqlite.Exec("delete from builds;") - - for _, build := range []*library.Build{_buildTwo, _buildOne} { - // create the build in the database - err := _database.CreateBuild(build) - if err != nil { - t.Errorf("unable to create test build: %v", err) - } - } - - got, _, err := _database.GetOrgBuildList("foo", filters, 1, 10) - - if test.failure { - if err == nil { - t.Errorf("GetOrgBuildListByEvent should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("GetOrgBuildListByEvent returned err: %v", err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetOrgBuildListByEvent is %v, want %v", got, test.want) - } - } -} - -func TestSqlite_Client_GetRepoBuildList(t *testing.T) { - // setup types - _buildOne := testBuild() - _buildOne.SetID(1) - _buildOne.SetRepoID(1) - _buildOne.SetNumber(1) - _buildOne.SetDeployPayload(nil) - _buildOne.SetCreated(1) - - _buildTwo := testBuild() - _buildTwo.SetID(2) - _buildTwo.SetRepoID(1) - _buildTwo.SetNumber(2) - _buildTwo.SetDeployPayload(nil) - _buildTwo.SetCreated(2) - - _repo := testRepo() - _repo.SetID(1) - _repo.SetUserID(1) - _repo.SetHash("baz") - _repo.SetOrg("foo") - _repo.SetName("bar") - _repo.SetFullName("foo/bar") - _repo.SetVisibility("public") - - // setup the test database client - _database, err := NewTest() - if err != nil { - t.Errorf("unable to create new sqlite test database: %v", err) - } - - defer func() { _sql, _ := _database.Sqlite.DB(); _sql.Close() }() - - // setup tests - tests := []struct { - failure bool - want []*library.Build - }{ - { - failure: false, - want: []*library.Build{_buildTwo, _buildOne}, - }, - } - - filters := map[string]interface{}{} - - // run tests - for _, test := range tests { - // defer cleanup of the repos table - defer _database.Sqlite.Exec("delete from repos;") - - // create the repo in the database - err := _database.CreateRepo(_repo) - if err != nil { - t.Errorf("unable to create test repo: %v", err) - } - - // defer cleanup of the builds table - defer _database.Sqlite.Exec("delete from builds;") - - for _, build := range test.want { - // create the build in the database - err := _database.CreateBuild(build) - if err != nil { - t.Errorf("unable to create test build: %v", err) - } - } - - got, _, err := _database.GetRepoBuildList(_repo, filters, time.Now().UTC().Unix(), 0, 1, 10) - - if test.failure { - if err == nil { - t.Errorf("GetRepoBuildList should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("GetRepoBuildList returned err: %v", err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetRepoBuildList is %v, want %v", got, test.want) - } - } -} diff --git a/database/sqlite/build_test.go b/database/sqlite/build_test.go deleted file mode 100644 index a0e8eb8c1..000000000 --- a/database/sqlite/build_test.go +++ /dev/null @@ -1,611 +0,0 @@ -// Copyright (c) 2022 Target Brands, Inc. All rights reserved. -// -// Use of this source code is governed by the LICENSE file in this repository. - -package sqlite - -import ( - "reflect" - "testing" - - "github.com/go-vela/types/library" -) - -func TestSqlite_Client_GetBuild(t *testing.T) { - // setup types - _build := testBuild() - _build.SetID(1) - _build.SetRepoID(1) - _build.SetNumber(1) - _build.SetDeployPayload(nil) - - _repo := testRepo() - _repo.SetID(1) - _repo.SetUserID(1) - _repo.SetHash("baz") - _repo.SetOrg("foo") - _repo.SetName("bar") - _repo.SetFullName("foo/bar") - _repo.SetVisibility("public") - - // setup the test database client - _database, err := NewTest() - if err != nil { - t.Errorf("unable to create new sqlite test database: %v", err) - } - - defer func() { _sql, _ := _database.Sqlite.DB(); _sql.Close() }() - - // setup tests - tests := []struct { - failure bool - want *library.Build - }{ - { - failure: false, - want: _build, - }, - { - failure: true, - want: nil, - }, - } - - // run tests - for _, test := range tests { - if test.want != nil { - // create the build in the database - err := _database.CreateBuild(test.want) - if err != nil { - t.Errorf("unable to create test build: %v", err) - } - } - - got, err := _database.GetBuild(1, _repo) - - // cleanup the builds table - _ = _database.Sqlite.Exec("DELETE FROM builds;") - - if test.failure { - if err == nil { - t.Errorf("GetBuild should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("GetBuild returned err: %v", err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetBuild is %v, want %v", got, test.want) - } - } -} - -func TestSqlite_Client_GetBuildByID(t *testing.T) { - // setup types - _build := testBuild() - _build.SetID(1) - _build.SetRepoID(1) - _build.SetNumber(1) - _build.SetDeployPayload(nil) - - // setup the test database client - _database, err := NewTest() - if err != nil { - t.Errorf("unable to create new sqlite test database: %v", err) - } - - defer func() { _sql, _ := _database.Sqlite.DB(); _sql.Close() }() - - // setup tests - tests := []struct { - failure bool - want *library.Build - }{ - { - failure: false, - want: _build, - }, - { - failure: true, - want: nil, - }, - } - - // run tests - for _, test := range tests { - if test.want != nil { - // create the build in the database - err := _database.CreateBuild(test.want) - if err != nil { - t.Errorf("unable to create test build: %v", err) - } - } - - got, err := _database.GetBuildByID(1) - - // cleanup the builds table - _ = _database.Sqlite.Exec("DELETE FROM builds;") - - if test.failure { - if err == nil { - t.Errorf("GetBuildByID should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("GetBuildByID returned err: %v", err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetBuildByID is %v, want %v", got, test.want) - } - } -} - -func TestSqlite_Client_GetLastBuild(t *testing.T) { - // setup types - _build := testBuild() - _build.SetID(1) - _build.SetRepoID(1) - _build.SetNumber(1) - _build.SetDeployPayload(nil) - - _repo := testRepo() - _repo.SetID(1) - _repo.SetUserID(1) - _repo.SetHash("baz") - _repo.SetOrg("foo") - _repo.SetName("bar") - _repo.SetFullName("foo/bar") - _repo.SetVisibility("public") - - // setup the test database client - _database, err := NewTest() - if err != nil { - t.Errorf("unable to create new sqlite test database: %v", err) - } - - defer func() { _sql, _ := _database.Sqlite.DB(); _sql.Close() }() - - // setup tests - tests := []struct { - failure bool - want *library.Build - }{ - { - failure: false, - want: _build, - }, - { - failure: false, - want: nil, - }, - } - - // run tests - for _, test := range tests { - if test.want != nil { - // create the build in the database - err := _database.CreateBuild(test.want) - if err != nil { - t.Errorf("unable to create test build: %v", err) - } - } - - got, err := _database.GetLastBuild(_repo) - - // cleanup the builds table - _ = _database.Sqlite.Exec("DELETE FROM builds;") - - if test.failure { - if err == nil { - t.Errorf("GetLastBuild should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("GetLastBuild returned err: %v", err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetLastBuild is %v, want %v", got, test.want) - } - } -} - -func TestSqlite_Client_GetLastBuildByBranch(t *testing.T) { - // setup types - _build := testBuild() - _build.SetID(1) - _build.SetRepoID(1) - _build.SetNumber(1) - _build.SetBranch("master") - _build.SetDeployPayload(nil) - - _repo := testRepo() - _repo.SetID(1) - _repo.SetUserID(1) - _repo.SetHash("baz") - _repo.SetOrg("foo") - _repo.SetName("bar") - _repo.SetFullName("foo/bar") - _repo.SetVisibility("public") - - // setup the test database client - _database, err := NewTest() - if err != nil { - t.Errorf("unable to create new sqlite test database: %v", err) - } - - defer func() { _sql, _ := _database.Sqlite.DB(); _sql.Close() }() - - // setup tests - tests := []struct { - failure bool - want *library.Build - }{ - { - failure: false, - want: _build, - }, - { - failure: false, - want: nil, - }, - } - - // run tests - for _, test := range tests { - if test.want != nil { - // create the build in the database - err := _database.CreateBuild(test.want) - if err != nil { - t.Errorf("unable to create test build: %v", err) - } - } - - got, err := _database.GetLastBuildByBranch(_repo, "master") - - // cleanup the builds table - _ = _database.Sqlite.Exec("DELETE FROM builds;") - - if test.failure { - if err == nil { - t.Errorf("GetLastBuildByBranch should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("GetLastBuildByBranch returned err: %v", err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetLastBuildByBranch is %v, want %v", got, test.want) - } - } -} - -func TestSqlite_Client_GetPendingAndRunningBuilds(t *testing.T) { - // setup types - _buildOne := testBuild() - _buildOne.SetID(1) - _buildOne.SetRepoID(1) - _buildOne.SetNumber(1) - _buildOne.SetStatus("running") - _buildOne.SetCreated(1) - _buildOne.SetDeployPayload(nil) - - _buildTwo := testBuild() - _buildTwo.SetID(2) - _buildTwo.SetRepoID(1) - _buildTwo.SetNumber(2) - _buildTwo.SetStatus("pending") - _buildTwo.SetCreated(1) - _buildTwo.SetDeployPayload(nil) - - _queueOne := new(library.BuildQueue) - _queueOne.SetCreated(1) - _queueOne.SetFullName("foo/bar") - _queueOne.SetNumber(1) - _queueOne.SetStatus("running") - - _queueTwo := new(library.BuildQueue) - _queueTwo.SetCreated(1) - _queueTwo.SetFullName("foo/bar") - _queueTwo.SetNumber(2) - _queueTwo.SetStatus("pending") - - _repo := testRepo() - _repo.SetID(1) - _repo.SetUserID(1) - _repo.SetHash("baz") - _repo.SetOrg("foo") - _repo.SetName("bar") - _repo.SetFullName("foo/bar") - _repo.SetVisibility("public") - - // setup the test database client - _database, err := NewTest() - if err != nil { - t.Errorf("unable to create new sqlite test database: %v", err) - } - - defer func() { _sql, _ := _database.Sqlite.DB(); _sql.Close() }() - - // setup tests - tests := []struct { - failure bool - want []*library.BuildQueue - }{ - { - failure: false, - want: []*library.BuildQueue{_queueOne, _queueTwo}, - }, - { - failure: false, - want: []*library.BuildQueue{}, - }, - } - - // run tests - for _, test := range tests { - // create the repo in the database - err := _database.CreateRepo(_repo) - if err != nil { - t.Errorf("unable to create test repo: %v", err) - } - - if len(test.want) > 0 { - // create the builds in the database - err = _database.CreateBuild(_buildOne) - if err != nil { - t.Errorf("unable to create test build: %v", err) - } - - err = _database.CreateBuild(_buildTwo) - if err != nil { - t.Errorf("unable to create test build: %v", err) - } - } - - got, err := _database.GetPendingAndRunningBuilds("0") - - // cleanup the repos table - _ = _database.Sqlite.Exec("DELETE FROM repos;") - // cleanup the builds table - _ = _database.Sqlite.Exec("DELETE FROM builds;") - - if test.failure { - if err == nil { - t.Errorf("GetPendingAndRunningBuilds should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("GetPendingAndRunningBuilds returned err: %v", err) - } - - if !reflect.DeepEqual(got, test.want) { - t.Errorf("GetPendingAndRunningBuilds is %v, want %v", got, test.want) - } - } -} - -func TestSqlite_Client_CreateBuild(t *testing.T) { - // setup types - _build := testBuild() - _build.SetID(1) - _build.SetRepoID(1) - _build.SetNumber(1) - - _repo := testRepo() - _repo.SetID(1) - _repo.SetUserID(1) - _repo.SetHash("baz") - _repo.SetOrg("foo") - _repo.SetName("bar") - _repo.SetFullName("foo/bar") - _repo.SetVisibility("public") - - // setup the test database client - _database, err := NewTest() - if err != nil { - t.Errorf("unable to create new sqlite test database: %v", err) - } - - defer func() { _sql, _ := _database.Sqlite.DB(); _sql.Close() }() - - // setup tests - tests := []struct { - failure bool - }{ - { - failure: false, - }, - } - - // run tests - for _, test := range tests { - // defer cleanup of the builds table - defer _database.Sqlite.Exec("delete from builds;") - - err := _database.CreateBuild(_build) - - if test.failure { - if err == nil { - t.Errorf("CreateBuild should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("CreateBuild returned err: %v", err) - } - } -} - -func TestSqlite_Client_UpdateBuild(t *testing.T) { - // setup types - _build := testBuild() - _build.SetID(1) - _build.SetRepoID(1) - _build.SetNumber(1) - - _repo := testRepo() - _repo.SetID(1) - _repo.SetUserID(1) - _repo.SetHash("baz") - _repo.SetOrg("foo") - _repo.SetName("bar") - _repo.SetFullName("foo/bar") - _repo.SetVisibility("public") - - // setup the test database client - _database, err := NewTest() - if err != nil { - t.Errorf("unable to create new sqlite test database: %v", err) - } - - defer func() { _sql, _ := _database.Sqlite.DB(); _sql.Close() }() - - // setup tests - tests := []struct { - failure bool - }{ - { - failure: false, - }, - } - - // run tests - for _, test := range tests { - // defer cleanup of the builds table - defer _database.Sqlite.Exec("delete from builds;") - - // create the build in the database - err = _database.CreateBuild(_build) - if err != nil { - t.Errorf("unable to create test build: %v", err) - } - - err := _database.UpdateBuild(_build) - - if test.failure { - if err == nil { - t.Errorf("UpdateBuild should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("UpdateBuild returned err: %v", err) - } - } -} - -func TestSqlite_Client_DeleteBuild(t *testing.T) { - // setup types - _build := testBuild() - _build.SetID(1) - _build.SetRepoID(1) - _build.SetNumber(1) - - // setup the test database client - _database, err := NewTest() - if err != nil { - t.Errorf("unable to create new sqlite test database: %v", err) - } - - defer func() { _sql, _ := _database.Sqlite.DB(); _sql.Close() }() - - // setup tests - tests := []struct { - failure bool - }{ - { - failure: false, - }, - } - - // run tests - for _, test := range tests { - // defer cleanup of the builds table - defer _database.Sqlite.Exec("delete from builds;") - - // create the build in the database - err = _database.CreateBuild(_build) - if err != nil { - t.Errorf("unable to create test build: %v", err) - } - - err = _database.DeleteBuild(1) - - if test.failure { - if err == nil { - t.Errorf("DeleteBuild should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("DeleteBuild returned err: %v", err) - } - } -} - -// testBuild is a test helper function to create a -// library Build type with all fields set to their -// zero values. -func testBuild() *library.Build { - i64 := int64(0) - i := 0 - str := "" - - return &library.Build{ - ID: &i64, - RepoID: &i64, - PipelineID: &i64, - Number: &i, - Parent: &i, - Event: &str, - EventAction: &str, - Status: &str, - Error: &str, - Enqueued: &i64, - Created: &i64, - Started: &i64, - Finished: &i64, - Deploy: &str, - Clone: &str, - Source: &str, - Title: &str, - Message: &str, - Commit: &str, - Sender: &str, - Author: &str, - Email: &str, - Link: &str, - Branch: &str, - Ref: &str, - BaseRef: &str, - HeadRef: &str, - Host: &str, - Runtime: &str, - Distribution: &str, - } -} diff --git a/database/sqlite/ddl/build.go b/database/sqlite/ddl/build.go deleted file mode 100644 index e4a713fdd..000000000 --- a/database/sqlite/ddl/build.go +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (c) 2022 Target Brands, Inc. All rights reserved. -// -// Use of this source code is governed by the LICENSE file in this repository. - -package ddl - -const ( - // CreateBuildTable represents a query to - // create the builds table for Vela. - CreateBuildTable = ` -CREATE TABLE -IF NOT EXISTS -builds ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - repo_id INTEGER, - pipeline_id INTEGER, - number INTEGER, - parent INTEGER, - event TEXT, - event_action TEXT, - status TEXT, - error TEXT, - enqueued INTEGER, - created INTEGER, - started INTEGER, - finished INTEGER, - deploy TEXT, - deploy_payload TEXT, - clone TEXT, - source TEXT, - title TEXT, - message TEXT, - 'commit' TEXT, - sender TEXT, - author TEXT, - email TEXT, - link TEXT, - branch TEXT, - ref TEXT, - base_ref TEXT, - head_ref TEXT, - host TEXT, - runtime TEXT, - distribution TEXT, - timestamp INTEGER, - UNIQUE(repo_id, number) -); -` - - // CreateBuildRepoIDIndex represents a query to create an - // index on the builds table for the repo_id column. - CreateBuildRepoIDIndex = ` -CREATE INDEX -IF NOT EXISTS -builds_repo_id -ON builds (repo_id); -` - - // CreateBuildStatusIndex represents a query to create an - // index on the builds table for the status column. - CreateBuildStatusIndex = ` -CREATE INDEX -IF NOT EXISTS -builds_status -ON builds (status); -` - - // CreateBuildCreatedIndex represents a query to create an - // index on the builds table for the created column. - CreateBuildCreatedIndex = ` -CREATE INDEX -IF NOT EXISTS -builds_created -ON builds (created); -` - - // CreateBuildSourceIndex represents a query to create an - // index on the builds table for the source column. - CreateBuildSourceIndex = ` -CREATE INDEX -IF NOT EXISTS -builds_source -ON builds (source); -` -) diff --git a/database/sqlite/ddl/doc.go b/database/sqlite/ddl/doc.go deleted file mode 100644 index 3e2bd5d2b..000000000 --- a/database/sqlite/ddl/doc.go +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) 2022 Target Brands, Inc. All rights reserved. -// -// Use of this source code is governed by the LICENSE file in this repository. - -// Package ddl provides the Sqlite data definition language (DDL) for Vela. -// -// https://en.wikipedia.org/wiki/Data_definition_language -// -// Usage: -// -// import "github.com/go-vela/server/database/sqlite/ddl" -package ddl diff --git a/database/sqlite/dml/build.go b/database/sqlite/dml/build.go deleted file mode 100644 index fa9f94453..000000000 --- a/database/sqlite/dml/build.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) 2022 Target Brands, Inc. All rights reserved. -// -// Use of this source code is governed by the LICENSE file in this repository. - -package dml - -const ( - // ListBuilds represents a query to - // list all builds in the database. - ListBuilds = ` -SELECT * -FROM builds; -` - - // SelectBuildByID represents a query to select - // a build for its id in the database. - SelectBuildByID = ` -SELECT * -FROM builds -WHERE id = ? -LIMIT 1; -` - - // SelectRepoBuild represents a query to select - // a build for a repo_id in the database. - SelectRepoBuild = ` -SELECT * -FROM builds -WHERE repo_id = ? -AND number = ? -LIMIT 1; -` - - // SelectLastRepoBuild represents a query to select - // the last build for a repo_id in the database. - SelectLastRepoBuild = ` -SELECT * -FROM builds -WHERE repo_id = ? -ORDER BY number DESC -LIMIT 1; -` - // SelectLastRepoBuildByBranch represents a query to - // select the last build for a repo_id and branch name - // in the database. - SelectLastRepoBuildByBranch = ` -SELECT * -FROM builds -WHERE repo_id = ? -AND branch = ? -ORDER BY number DESC -LIMIT 1; -` - - // SelectBuildsCount represents a query to select - // the count of builds in the database. - SelectBuildsCount = ` -SELECT count(*) as count -FROM builds; -` - - // SelectBuildsCountByStatus represents a query to select - // the count of builds for a status in the database. - SelectBuildsCountByStatus = ` -SELECT count(*) as count -FROM builds -WHERE status = ?; -` - - // DeleteBuild represents a query to - // remove a build from the database. - DeleteBuild = ` -DELETE -FROM builds -WHERE id = ?; -` - - // SelectPendingAndRunningBuilds represents a joined query - // between the builds & repos table to select - // the created builds that are in pending or running builds status - // since the specified timeframe. - SelectPendingAndRunningBuilds = ` -SELECT builds.created, builds.number, builds.status, repos.full_name -FROM builds INNER JOIN repos -ON builds.repo_id = repos.id -WHERE builds.created > ? -AND (builds.status = 'running' OR builds.status = 'pending'); -` -) diff --git a/database/sqlite/dml/doc.go b/database/sqlite/dml/doc.go deleted file mode 100644 index 151d8a9a5..000000000 --- a/database/sqlite/dml/doc.go +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) 2022 Target Brands, Inc. All rights reserved. -// -// Use of this source code is governed by the LICENSE file in this repository. - -// Package dml provides the Sqlite data manipulation language (DML) for Vela. -// -// https://en.wikipedia.org/wiki/Data_manipulation_language -// -// Usage: -// -// import "github.com/go-vela/server/database/sqlite/dml" -package dml diff --git a/database/sqlite/sqlite.go b/database/sqlite/sqlite.go index 3faa555ba..29f526643 100644 --- a/database/sqlite/sqlite.go +++ b/database/sqlite/sqlite.go @@ -5,9 +5,9 @@ package sqlite import ( - "fmt" "time" + "github.com/go-vela/server/database/build" "github.com/go-vela/server/database/hook" "github.com/go-vela/server/database/log" "github.com/go-vela/server/database/pipeline" @@ -15,11 +15,9 @@ import ( "github.com/go-vela/server/database/schedule" "github.com/go-vela/server/database/secret" "github.com/go-vela/server/database/service" - "github.com/go-vela/server/database/sqlite/ddl" "github.com/go-vela/server/database/step" "github.com/go-vela/server/database/user" "github.com/go-vela/server/database/worker" - "github.com/go-vela/types/constants" "github.com/sirupsen/logrus" "gorm.io/driver/sqlite" @@ -50,6 +48,8 @@ type ( Sqlite *gorm.DB // https://pkg.go.dev/github.com/sirupsen/logrus#Entry Logger *logrus.Entry + // https://pkg.go.dev/github.com/go-vela/server/database/build#BuildInterface + build.BuildInterface // https://pkg.go.dev/github.com/go-vela/server/database/hook#HookInterface hook.HookInterface // https://pkg.go.dev/github.com/go-vela/server/database/log#LogInterface @@ -177,12 +177,6 @@ func NewTest() (*client, error) { return nil, err } - // create the tables in the database - err = createTables(c) - if err != nil { - return nil, err - } - return c, nil } @@ -225,64 +219,6 @@ func setupDatabase(c *client) error { return nil } - // create the tables in the database - err = createTables(c) - if err != nil { - return err - } - - // create the indexes in the database - err = createIndexes(c) - if err != nil { - return err - } - - return nil -} - -// createTables is a helper function to setup -// the database with the necessary tables. -func createTables(c *client) error { - c.Logger.Trace("creating data tables in the sqlite database") - - // create the builds table - err := c.Sqlite.Exec(ddl.CreateBuildTable).Error - if err != nil { - return fmt.Errorf("unable to create %s table: %w", constants.TableBuild, err) - } - - return nil -} - -// createIndexes is a helper function to setup -// the database with the necessary indexes. -func createIndexes(c *client) error { - c.Logger.Trace("creating data indexes in the sqlite database") - - // create the builds_repo_id index for the builds table - err := c.Sqlite.Exec(ddl.CreateBuildRepoIDIndex).Error - if err != nil { - return fmt.Errorf("unable to create builds_repo_id index for the %s table: %w", constants.TableBuild, err) - } - - // create the builds_status index for the builds table - err = c.Sqlite.Exec(ddl.CreateBuildStatusIndex).Error - if err != nil { - return fmt.Errorf("unable to create builds_status index for the %s table: %w", constants.TableBuild, err) - } - - // create the builds_created index for the builds table - err = c.Sqlite.Exec(ddl.CreateBuildCreatedIndex).Error - if err != nil { - return fmt.Errorf("unable to create builds_created index for the %s table: %w", constants.TableBuild, err) - } - - // create the builds_source index for the builds table - err = c.Sqlite.Exec(ddl.CreateBuildSourceIndex).Error - if err != nil { - return fmt.Errorf("unable to create builds_source index for the %s table: %w", constants.TableBuild, err) - } - return nil } @@ -290,6 +226,18 @@ func createIndexes(c *client) error { func createServices(c *client) error { var err error + // create the database agnostic engine for builds + // + // https://pkg.go.dev/github.com/go-vela/server/database/build#New + c.BuildInterface, err = build.New( + build.WithClient(c.Sqlite), + build.WithLogger(c.Logger), + build.WithSkipCreation(c.config.SkipCreation), + ) + if err != nil { + return err + } + // create the database agnostic engine for hooks // // https://pkg.go.dev/github.com/go-vela/server/database/hook#New diff --git a/database/sqlite/sqlite_test.go b/database/sqlite/sqlite_test.go index 48ff34902..55c6940d0 100644 --- a/database/sqlite/sqlite_test.go +++ b/database/sqlite/sqlite_test.go @@ -7,8 +7,6 @@ package sqlite import ( "testing" "time" - - "github.com/go-vela/types/library" ) func TestSqlite_New(t *testing.T) { @@ -111,78 +109,6 @@ func TestSqlite_setupDatabase(t *testing.T) { } } -func TestSqlite_createTables(t *testing.T) { - // setup types - // setup the test database client - _database, err := NewTest() - if err != nil { - t.Errorf("unable to create new sqlite test database: %v", err) - } - - defer func() { _sql, _ := _database.Sqlite.DB(); _sql.Close() }() - - tests := []struct { - failure bool - }{ - { - failure: false, - }, - } - - // run tests - for _, test := range tests { - err := createTables(_database) - - if test.failure { - if err == nil { - t.Errorf("createTables should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("createTables returned err: %v", err) - } - } -} - -func TestSqlite_createIndexes(t *testing.T) { - // setup types - // setup the test database client - _database, err := NewTest() - if err != nil { - t.Errorf("unable to create new sqlite test database: %v", err) - } - - defer func() { _sql, _ := _database.Sqlite.DB(); _sql.Close() }() - - tests := []struct { - failure bool - }{ - { - failure: false, - }, - } - - // run tests - for _, test := range tests { - err := createIndexes(_database) - - if test.failure { - if err == nil { - t.Errorf("createIndexes should have returned err") - } - - continue - } - - if err != nil { - t.Errorf("createIndexes returned err: %v", err) - } - } -} - func TestSqlite_createServices(t *testing.T) { // setup types // setup the test database client @@ -218,38 +144,3 @@ func TestSqlite_createServices(t *testing.T) { } } } - -// testRepo is a test helper function to create a -// library Repo type with all fields set to their -// zero values. -func testRepo() *library.Repo { - i64 := int64(0) - i := 0 - str := "" - b := false - - return &library.Repo{ - ID: &i64, - UserID: &i64, - Hash: &str, - Org: &str, - Name: &str, - FullName: &str, - Link: &str, - Clone: &str, - Branch: &str, - BuildLimit: &i64, - Timeout: &i64, - Counter: &i, - Visibility: &str, - Private: &b, - Trusted: &b, - Active: &b, - AllowPull: &b, - AllowPush: &b, - AllowDeploy: &b, - AllowTag: &b, - AllowComment: &b, - PreviousName: &str, - } -} diff --git a/router/middleware/build/build.go b/router/middleware/build/build.go index 87b25185b..6016adf3c 100644 --- a/router/middleware/build/build.go +++ b/router/middleware/build/build.go @@ -64,7 +64,7 @@ func Establish() gin.HandlerFunc { "user": u.GetName(), }).Debugf("reading build %s/%d", r.GetFullName(), number) - b, err := database.FromContext(c).GetBuild(number, r) + b, err := database.FromContext(c).GetBuildForRepo(r, number) if err != nil { retErr := fmt.Errorf("unable to read build %s/%d: %w", r.GetFullName(), number, err) util.HandleError(c, http.StatusNotFound, retErr)