Skip to content

Commit 64a6f0d

Browse files
authored
feat/drift fields (#2018)
* update fields * add drift settings and drift updates per project * address reviews * add drift fields
1 parent c565c98 commit 64a6f0d

File tree

9 files changed

+170
-12
lines changed

9 files changed

+170
-12
lines changed

backend/bootstrap/main.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,12 +225,17 @@ func Bootstrap(templates embed.FS, diggerController controllers.DiggerController
225225
apiGroup := r.Group("/api")
226226
apiGroup.Use(middleware.InternalApiAuth(), middleware.HeadersApiAuth())
227227

228+
orgsApiGroup := apiGroup.Group("/orgs")
229+
orgsApiGroup.GET("/settings/", controllers.GetOrgSettingsApi)
230+
orgsApiGroup.PUT("/settings/", controllers.UpdateOrgSettingsApi)
231+
228232
reposApiGroup := apiGroup.Group("/repos")
229233
reposApiGroup.GET("/", controllers.ListReposApi)
230234
reposApiGroup.GET("/:repo_id/jobs", controllers.GetJobsForRepoApi)
231235

232236
projectsApiGroup := apiGroup.Group("/projects")
233237
projectsApiGroup.GET("/", controllers.ListProjectsApi)
238+
projectsApiGroup.PUT("/:project_id/", controllers.UpdateProjectApi)
234239

235240
githubApiGroup := apiGroup.Group("/github")
236241
githubApiGroup.POST("/link", controllers.LinkGithubInstallationToOrgApi)

backend/controllers/orgs.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
package controllers
22

33
import (
4+
"encoding/json"
5+
"errors"
46
"fmt"
7+
"github.com/diggerhq/digger/backend/middleware"
8+
"gorm.io/gorm"
59
"log/slog"
610
"net/http"
711
"os"
@@ -17,6 +21,70 @@ type TenantCreatedEvent struct {
1721
Name string `json:"name,omitempty"`
1822
}
1923

24+
func GetOrgSettingsApi(c *gin.Context) {
25+
organisationId := c.GetString(middleware.ORGANISATION_ID_KEY)
26+
organisationSource := c.GetString(middleware.ORGANISATION_SOURCE_KEY)
27+
28+
var org models.Organisation
29+
err := models.DB.GormDB.Where("external_id = ? AND external_source = ?", organisationId, organisationSource).First(&org).Error
30+
if err != nil {
31+
if errors.Is(err, gorm.ErrRecordNotFound) {
32+
slog.Info("Organisation not found", "organisationId", organisationId, "source", organisationSource)
33+
c.String(http.StatusNotFound, "Could not find organisation: "+organisationId)
34+
} else {
35+
slog.Error("Error fetching organisation", "organisationId", organisationId, "source", organisationSource, "error", err)
36+
c.String(http.StatusInternalServerError, "Error fetching organisation")
37+
}
38+
return
39+
}
40+
41+
c.JSON(http.StatusOK, gin.H{
42+
"drift_enabled": org.DriftEnabled,
43+
"drift_cron_tab": org.DriftCronTab,
44+
"drift_webhook_url": org.DriftWebhookUrl,
45+
})
46+
}
47+
48+
func UpdateOrgSettingsApi(c *gin.Context) {
49+
organisationId := c.GetString(middleware.ORGANISATION_ID_KEY)
50+
organisationSource := c.GetString(middleware.ORGANISATION_SOURCE_KEY)
51+
52+
var org models.Organisation
53+
err := models.DB.GormDB.Where("external_id = ? AND external_source = ?", organisationId, organisationSource).First(&org).Error
54+
if err != nil {
55+
if errors.Is(err, gorm.ErrRecordNotFound) {
56+
slog.Info("Organisation not found", "organisationId", organisationId, "source", organisationSource)
57+
c.String(http.StatusNotFound, "Could not find organisation: "+organisationId)
58+
} else {
59+
slog.Error("Error fetching organisation", "organisationId", organisationId, "source", organisationSource, "error", err)
60+
c.String(http.StatusInternalServerError, "Error fetching organisation")
61+
}
62+
return
63+
}
64+
var reqBody struct {
65+
DriftEnabled bool `json:"drift_enabled"`
66+
DriftCronTab string `json:"drift_cron_tab"`
67+
DriftWebhookUrl string `json:"drift_webhook_url"`
68+
}
69+
err = json.NewDecoder(c.Request.Body).Decode(&reqBody)
70+
if err != nil {
71+
slog.Error("Error decoding request body", "error", err)
72+
c.String(http.StatusBadRequest, "Error decoding request body")
73+
return
74+
}
75+
76+
org.DriftEnabled = reqBody.DriftEnabled
77+
org.DriftCronTab = reqBody.DriftCronTab
78+
org.DriftWebhookUrl = reqBody.DriftWebhookUrl
79+
err = models.DB.GormDB.Save(&org).Error
80+
if err != nil {
81+
slog.Error("Error saving organisation", "organisationId", organisationId, "source", organisationSource, "error", err)
82+
c.String(http.StatusInternalServerError, "Error saving organisation")
83+
return
84+
}
85+
86+
c.JSON(http.StatusOK, gin.H{})
87+
}
2088
func CreateFronteggOrgFromWebhook(c *gin.Context) {
2189
var json TenantCreatedEvent
2290

backend/controllers/projects.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,58 @@ func ListProjectsApi(c *gin.Context) {
7171
c.JSON(http.StatusOK, response)
7272
}
7373

74+
func UpdateProjectApi(c *gin.Context) {
75+
// assume all exists as validated in middleware
76+
organisationId := c.GetString(middleware.ORGANISATION_ID_KEY)
77+
organisationSource := c.GetString(middleware.ORGANISATION_SOURCE_KEY)
78+
projectId := c.Param("project_id")
79+
80+
var org models.Organisation
81+
err := models.DB.GormDB.Where("external_id = ? AND external_source = ?", organisationId, organisationSource).First(&org).Error
82+
if err != nil {
83+
if errors.Is(err, gorm.ErrRecordNotFound) {
84+
slog.Info("Organisation not found", "organisationId", organisationId, "source", organisationSource)
85+
c.String(http.StatusNotFound, "Could not find organisation: "+organisationId)
86+
} else {
87+
slog.Error("Error fetching organisation", "organisationId", organisationId, "source", organisationSource, "error", err)
88+
c.String(http.StatusInternalServerError, "Error fetching organisation")
89+
}
90+
return
91+
}
92+
var reqBody struct {
93+
DriftEnabled bool `json:"drift_enabled"`
94+
}
95+
err = json.NewDecoder(c.Request.Body).Decode(&reqBody)
96+
if err != nil {
97+
slog.Error("Error decoding request body", "error", err)
98+
c.String(http.StatusBadRequest, "Error decoding request body")
99+
return
100+
}
101+
102+
var project models.Project
103+
err = models.DB.GormDB.Where("projects.organisation_id = ? AND projects.id = ?", org.ID, projectId).First(&project).Error
104+
if err != nil {
105+
if errors.Is(err, gorm.ErrRecordNotFound) {
106+
slog.Info("Project not found", "organisationId", organisationId, "orgId", org.ID)
107+
c.String(http.StatusNotFound, "Could not find project")
108+
} else {
109+
slog.Error("Error fetching project", "organisationId", organisationId, "orgId", org.ID, "error", err)
110+
c.String(http.StatusInternalServerError, "Unknown error occurred while fetching database")
111+
}
112+
return
113+
}
114+
115+
project.DriftEnabled = reqBody.DriftEnabled
116+
err = models.DB.GormDB.Save(&project).Error
117+
if err != nil {
118+
slog.Error("Error updating project", "organisationId", organisationId, "orgId", org.ID, "error", err)
119+
c.String(http.StatusInternalServerError, "Unknown error occurred while updating database")
120+
return
121+
}
122+
123+
c.JSON(http.StatusOK, project)
124+
}
125+
74126
func FindProjectsForRepo(c *gin.Context) {
75127
repo := c.Param("repo")
76128
orgId, exists := c.Get(middleware.ORGANISATION_ID_KEY)

backend/migrations/20250710234046.sql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- Modify "projects" table
2+
ALTER TABLE "public"."projects" ADD COLUMN "drift_enabled" boolean NULL DEFAULT false, ADD COLUMN "drift_status" text NULL DEFAULT 'no drift', ADD COLUMN "latest_drift_check" timestamptz NULL, ADD COLUMN "drift_terraform_plan" text NULL;

backend/migrations/20250711030148.sql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- Modify "organisations" table
2+
ALTER TABLE "public"."organisations" ADD COLUMN "drift_webhook_url" text NULL, ADD COLUMN "drift_cron_tab" text NULL DEFAULT '0 0 * * *';

backend/migrations/20250711030248.sql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- Modify "organisations" table
2+
ALTER TABLE "public"."organisations" ADD COLUMN "drift_enabled" boolean NULL;

backend/migrations/20250711030323.sql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- Modify "organisations" table
2+
ALTER TABLE "public"."organisations" ALTER COLUMN "drift_enabled" SET DEFAULT false;

backend/migrations/atlas.sum

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
h1:E1MYgdCTYDdHVINjSBVtGufzvYspZC8Wur7nvPK7fN0=
1+
h1:94F268ZEPf2Y9xBgyPcV2kVk0fb4+6flqUIRqoaO4cs=
22
20231227132525.sql h1:43xn7XC0GoJsCnXIMczGXWis9d504FAWi4F1gViTIcw=
33
20240115170600.sql h1:IW8fF/8vc40+eWqP/xDK+R4K9jHJ9QBSGO6rN9LtfSA=
44
20240116123649.sql h1:R1JlUIgxxF6Cyob9HdtMqiKmx/BfnsctTl5rvOqssQw=
@@ -52,3 +52,7 @@ h1:E1MYgdCTYDdHVINjSBVtGufzvYspZC8Wur7nvPK7fN0=
5252
20250530015921.sql h1:FidRW+0ur3mCDBPgP7V/sqr2jjfmi/CFJPRpNxTmqGs=
5353
20250704024948.sql h1:ki8bOrfd8y7Mfq40Dz5lH6MUuQqPVah+oimeRDVHOOQ=
5454
20250704025208.sql h1:XRY66dphINSj53mbxl5ALmyWqebuB1swPvUuUPvNR88=
55+
20250710234046.sql h1:C+ADwlHOKQ+DiHoptVH/74utW8dN7ytY4ypfZkXzFeY=
56+
20250711030148.sql h1:hN972A3rU9TN4bCUo1muJWwEqNQQyIPlmYgsePNH00w=
57+
20250711030248.sql h1:oKzrMdfE75UyMSx++OMrXOAi1AOLN6kuv8iBLq8+srs=
58+
20250711030323.sql h1:vN9g0H99CItCw9aG6tRo73py5qQ4iD6qew2Y66WcoBk=

backend/models/orgs.go

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@ import (
88

99
type Organisation struct {
1010
gorm.Model
11-
Name string `gorm:"Index:idx_organisation"`
12-
ExternalSource string `gorm:"uniqueIndex:idx_external_source"`
13-
ExternalId string `gorm:"uniqueIndex:idx_external_source"`
11+
Name string `gorm:"Index:idx_organisation"`
12+
ExternalSource string `gorm:"uniqueIndex:idx_external_source"`
13+
ExternalId string `gorm:"uniqueIndex:idx_external_source"`
14+
DriftEnabled bool `gorm:"default:false"`
15+
DriftWebhookUrl string
16+
DriftCronTab string `gorm:"default:'0 0 * * *'"`
1417
}
1518

1619
type Repo struct {
@@ -71,16 +74,26 @@ const (
7174
ProjectInactive ProjectStatus = 2
7275
)
7376

77+
type DriftStatus string
78+
79+
const DriftStatusNewDrift DriftStatus = "new drift"
80+
const DriftStatusNoDrift DriftStatus = "no drift"
81+
const DriftStatusAcknowledgeDrift DriftStatus = "acknowledged drift"
82+
7483
type Project struct {
7584
gorm.Model
76-
Name string `gorm:"uniqueIndex:idx_project_org"`
77-
Directory string
78-
OrganisationID uint `gorm:"uniqueIndex:idx_project_org"`
79-
Organisation *Organisation
80-
RepoFullName string `gorm:"uniqueIndex:idx_project_org"`
81-
Status ProjectStatus
82-
IsGenerated bool
83-
IsInMainBranch bool
85+
Name string `gorm:"uniqueIndex:idx_project_org"`
86+
Directory string
87+
OrganisationID uint `gorm:"uniqueIndex:idx_project_org"`
88+
Organisation *Organisation
89+
RepoFullName string `gorm:"uniqueIndex:idx_project_org"`
90+
DriftEnabled bool `gorm:"default:false"`
91+
DriftStatus DriftStatus `gorm:"default:'no drift'"`
92+
LatestDriftCheck time.Time
93+
DriftTerraformPlan string
94+
Status ProjectStatus
95+
IsGenerated bool
96+
IsInMainBranch bool
8497
}
8598

8699
func (p *Project) MapToJsonStruct() interface{} {
@@ -99,6 +112,10 @@ func (p *Project) MapToJsonStruct() interface{} {
99112
RepoFullName string `json:"repo_full_name"`
100113
IsInMainBranch bool `json:"is_in_main_branch"`
101114
IsGenerated bool `json:"is_generated"`
115+
DriftEnabled bool `json:"drift_enabled"`
116+
DriftStatus string `json:"drift_status"`
117+
LatestDriftCheck string `json:"latest_drift_check"`
118+
DriftTerraformPlan string `json:"drift_terraform_plan"`
102119
LastActivityTimestamp string `json:"last_activity_timestamp"`
103120
LastActivityAuthor string `json:"last_activity_author"`
104121
LastActivityStatus string `json:"last_activity_status"`
@@ -109,6 +126,10 @@ func (p *Project) MapToJsonStruct() interface{} {
109126
OrganisationID: p.OrganisationID,
110127
OrganisationName: p.Organisation.Name,
111128
RepoFullName: p.RepoFullName,
129+
DriftEnabled: p.DriftEnabled,
130+
DriftStatus: string(p.DriftStatus),
131+
LatestDriftCheck: p.LatestDriftCheck.String(),
132+
DriftTerraformPlan: p.DriftTerraformPlan,
112133
LastActivityTimestamp: p.UpdatedAt.String(),
113134
LastActivityAuthor: "unknown",
114135
LastActivityStatus: string(status),

0 commit comments

Comments
 (0)