diff --git a/backend/bootstrap/main.go b/backend/bootstrap/main.go index 89fe732c..ec2a664d 100644 --- a/backend/bootstrap/main.go +++ b/backend/bootstrap/main.go @@ -220,13 +220,12 @@ func Bootstrap(templates embed.FS, diggerController controllers.DiggerController reposApiGroup := apiGroup.Group("/repos") reposApiGroup.GET("/", controllers.ListReposApi) + reposApiGroup.GET("/:repo_id/jobs", controllers.GetJobsForRepoApi) githubApiGroup := apiGroup.Group("/github") githubApiGroup.POST("/link", controllers.LinkGithubInstallationToOrgApi) } - fronteggWebhookProcessor.POST("/create-org-from-frontegg", controllers.CreateFronteggOrgFromWebhook) - return r } diff --git a/backend/controllers/dashboard.go b/backend/controllers/dashboard.go new file mode 100644 index 00000000..3784ff0a --- /dev/null +++ b/backend/controllers/dashboard.go @@ -0,0 +1,32 @@ +package controllers + +import ( + "errors" + "github.com/diggerhq/digger/backend/middleware" + "github.com/diggerhq/digger/backend/models" + "github.com/gin-gonic/gin" + "gorm.io/gorm" + "log" + "net/http" +) + +func GetDashboardStatusApi(c *gin.Context) { + organisationId := c.GetString(middleware.ORGANISATION_ID_KEY) + organisationSource := c.GetString(middleware.ORGANISATION_SOURCE_KEY) + + var org models.Organisation + err := models.DB.GormDB.Where("external_id = ? AND external_source = ?", organisationId, organisationSource).First(&org).Error + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + c.String(http.StatusNotFound, "Could not find organisation: "+organisationId) + } else { + log.Printf("could not fetch organisation: %v err: %v", organisationId, err) + c.String(http.StatusNotFound, "Could not fetch organisation: "+organisationId) + } + return + } + + response := make(map[string]interface{}) + + c.JSON(http.StatusOK, response) +} diff --git a/backend/controllers/jobs.go b/backend/controllers/jobs.go new file mode 100644 index 00000000..bdd9612e --- /dev/null +++ b/backend/controllers/jobs.go @@ -0,0 +1,69 @@ +package controllers + +import ( + "errors" + "github.com/diggerhq/digger/backend/middleware" + "github.com/diggerhq/digger/backend/models" + orchestrator_scheduler "github.com/diggerhq/digger/libs/scheduler" + "github.com/gin-gonic/gin" + "gorm.io/gorm" + "log" + "net/http" + "strconv" +) + +func GetJobsForRepoApi(c *gin.Context) { + organisationId := c.GetString(middleware.ORGANISATION_ID_KEY) + organisationSource := c.GetString(middleware.ORGANISATION_SOURCE_KEY) + repoId := c.Param("repo_id") + + if repoId == "" { + log.Printf("missing parameter: repo_full_name") + c.String(http.StatusBadRequest, "missing parameter: repo_full_name") + return + } + + var org models.Organisation + err := models.DB.GormDB.Where("external_id = ? AND external_source = ?", organisationId, organisationSource).First(&org).Error + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + log.Printf("could not find organisation: %v err: %v", organisationId, err) + c.String(http.StatusNotFound, "Could not find organisation: "+organisationId) + } else { + log.Printf("could not fetch organisation: %v err: %v", organisationId, err) + c.String(http.StatusNotFound, "Could not fetch organisation: "+organisationId) + } + return + } + + repo, err := models.DB.GetRepoById(org.ID, repoId) + if err != nil { + log.Printf("could not fetch repo details %v", err) + c.String(http.StatusInternalServerError, "Unknown error occurred while fetching jobs from database") + return + } + + jobsRes, err := models.DB.GetJobsByRepoName(org.ID, repo.RepoFullName) + if err != nil { + log.Printf("could not fetch job details") + c.String(http.StatusInternalServerError, "Unknown error occurred while fetching jobs from database") + return + } + + // update the values of "status" accordingly + for i, j := range jobsRes { + statusInt, err := strconv.Atoi(j.Status) + if err != nil { + log.Printf("could not convert status to string: job id: %v status: %v", j.ID, j.Status) + continue + } + statusI := orchestrator_scheduler.DiggerJobStatus(statusInt) + jobsRes[i].Status = statusI.ToString() + } + + response := make(map[string]interface{}) + response["repo"] = repo + response["jobs"] = jobsRes + + c.JSON(http.StatusOK, response) +} diff --git a/backend/models/queries.go b/backend/models/queries.go new file mode 100644 index 00000000..35919857 --- /dev/null +++ b/backend/models/queries.go @@ -0,0 +1,18 @@ +package models + +import "time" + +type JobQueryResult struct { + ID uint `gorm:"column:id"` + CreatedAt time.Time `gorm:"column:created_at"` + UpdatedAt time.Time `gorm:"column:updated_at"` + DeletedAt *time.Time `gorm:"column:deleted_at"` + DiggerJobID string `gorm:"column:digger_job_id"` + Status string `gorm:"column:status"` + WorkflowRunURL string `gorm:"column:workflow_run_url"` + WorkflowFile string `gorm:"column:workflow_file"` + TerraformOutput string `gorm:"column:terraform_output"` + PRNumber int `gorm:"column:pr_number"` + RepoFullName string `gorm:"column:repo_full_name"` + BranchName string `gorm:"column:branch_name"` +} diff --git a/backend/models/storage.go b/backend/models/storage.go index 08679fb2..24f09310 100644 --- a/backend/models/storage.go +++ b/backend/models/storage.go @@ -924,6 +924,27 @@ func (db *Database) GetDiggerJobsForBatch(batchId uuid.UUID) ([]DiggerJob, error return jobs, nil } +func (db *Database) GetJobsByRepoName(orgId uint, repoFullName string) ([]JobQueryResult, error) { + var results []JobQueryResult + + query := ` + SELECT + j.id, j.created_at, j.updated_at, j.deleted_at, + j.digger_job_id, j.status, j.workflow_run_url, + j.workflow_file, j.terraform_output, db.pr_number, db.repo_full_name, db.branch_name + FROM digger_jobs j, digger_batches db, organisations o, github_app_installation_links l + WHERE o.id = l.organisation_id + AND l.github_installation_id = db.github_installation_id + AND db.id = j.batch_id + AND o.id = ? + AND db.repo_full_name = ? + ORDER BY j.created_at + ` + + err := db.GormDB.Raw(query, orgId, repoFullName).Scan(&results).Error + return results, err +} + func (db *Database) GetDiggerJobsForBatchWithStatus(batchId uuid.UUID, status []scheduler.DiggerJobStatus) ([]DiggerJob, error) { jobs := make([]DiggerJob, 0) diff --git a/backend/models/storage_dashboard.go b/backend/models/storage_dashboard.go new file mode 100644 index 00000000..83d66b4b --- /dev/null +++ b/backend/models/storage_dashboard.go @@ -0,0 +1,61 @@ +package models + +import ( + "github.com/diggerhq/digger/libs/scheduler" + "time" +) + +func (db *Database) GetRepoCount(orgID uint) (int64, error) { + var count int64 + + thirtyDaysAgo := time.Now().AddDate(0, 0, -30) + + result := db.GormDB.Model(&Repo{}). + Where("organisation_id = ? AND created_at >= ?", orgID, thirtyDaysAgo). + Count(&count) + + if result.Error != nil { + return 0, result.Error + } + + return count, nil +} + +func (db *Database) GetJobsCountThisMonth(orgID uint) (int64, error) { + var count int64 + + thirtyDaysAgo := time.Now().AddDate(0, 0, -30) + + result := db.GormDB.Model(DiggerJob{}). + Joins("JOIN digger_batches ON digger_jobs.batch_id = digger_batches.id"). + Joins("JOIN github_app_installation_links ON digger_batches.github_installation_id = github_app_installation_links.github_installation_id"). + Joins("JOIN organisations ON github_app_installation_links.organisation_id = organisations.id"). + Where("digger_jobs.created_at >= ? AND organisations.id = ?", thirtyDaysAgo, orgID). + Count(&count) + + if result.Error != nil { + return 0, result.Error + } + + return count, nil +} + +func (db *Database) GetSuccessfulJobsCountThisMonth(orgID uint) (int64, error) { + var count int64 + + thirtyDaysAgo := time.Now().AddDate(0, 0, -30) + + result := db.GormDB.Model(DiggerJob{}). + Joins("JOIN digger_batches ON digger_jobs.batch_id = digger_batches.id"). + Joins("JOIN github_app_installation_links ON digger_batches.github_installation_id = github_app_installation_links.github_installation_id"). + Joins("JOIN organisations ON github_app_installation_links.organisation_id = organisations.id"). + Where("digger_jobs.created_at >= ? AND organisations.id = ?", thirtyDaysAgo, orgID). + Where("digger_jobs.status = ?", scheduler.DiggerJobSucceeded). + Count(&count) + + if result.Error != nil { + return 0, result.Error + } + + return count, nil +}