Skip to content

Commit f5f7f58

Browse files
feat/internal apis for users (#1885)
* add internal endpoints for creation of user and org * support internal api for users * Apply suggestions from code review Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * fix merge * add idx and fk * drop unique index on organisation name * make it upsert org * make endpoints behind flag --------- Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
1 parent 8c758fb commit f5f7f58

13 files changed

+143
-6
lines changed

backend/bootstrap/main.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,12 @@ func Bootstrap(templates embed.FS, diggerController controllers.DiggerController
221221
runsApiGroup.GET("/:run_id", controllers.RunDetails)
222222
runsApiGroup.POST("/:run_id/approve", controllers.ApproveRun)
223223

224-
// internal endpoints not meant to be exposed to public and protected behing webhook secret
225-
r.POST("_internal/update_repo_cache", middleware.WebhookAuth(), diggerController.UpdateRepoCache)
224+
// internal endpoints not meant to be exposed to public and protected behind webhook secret
225+
if enableInternal := os.Getenv("DIGGER_ENABLE_INTERNAL_ENDPOINTS"); enableInternal == "true" {
226+
r.POST("_internal/update_repo_cache", middleware.InternalApiAuth(), diggerController.UpdateRepoCache)
227+
r.POST("_internal/api/create_user", middleware.InternalApiAuth(), diggerController.CreateUserInternal)
228+
r.POST("_internal/api/upsert_org", middleware.InternalApiAuth(), diggerController.UpsertOrgInternal)
229+
}
226230

227231
fronteggWebhookProcessor.POST("/create-org-from-frontegg", controllers.CreateFronteggOrgFromWebhook)
228232

backend/controllers/internal_users.go

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package controllers
2+
3+
import (
4+
"github.com/diggerhq/digger/backend/models"
5+
"github.com/gin-gonic/gin"
6+
"log"
7+
"net/http"
8+
)
9+
10+
func (d DiggerController) UpsertOrgInternal(c *gin.Context) {
11+
type OrgCreateRequest struct {
12+
Name string `json:"org_name"`
13+
ExternalSource string `json:"external_source"`
14+
ExternalId string `json:"external_id"`
15+
}
16+
17+
var request OrgCreateRequest
18+
err := c.BindJSON(&request)
19+
if err != nil {
20+
log.Printf("Error binding JSON: %v", err)
21+
c.JSON(http.StatusBadRequest, gin.H{"error": "Error binding JSON"})
22+
return
23+
}
24+
25+
name := request.Name
26+
externalSource := request.ExternalSource
27+
externalId := request.ExternalId
28+
29+
log.Printf("creating org for %v %v %v", name, externalSource, externalId)
30+
var org *models.Organisation
31+
org, err = models.DB.GetOrganisation(externalId)
32+
if err != nil {
33+
log.Printf("Error while retriving org: %v", err)
34+
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error creating org"})
35+
return
36+
}
37+
38+
if org == nil {
39+
org, err = models.DB.CreateOrganisation(name, externalSource, externalId)
40+
}
41+
42+
if err != nil {
43+
log.Printf("Error creating org: %v", err)
44+
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error creating org"})
45+
return
46+
}
47+
48+
c.JSON(http.StatusOK, gin.H{"status": "success", "org_id": org.ID})
49+
}
50+
51+
func (d DiggerController) CreateUserInternal(c *gin.Context) {
52+
type UserCreateRequest struct {
53+
UserExternalSource string `json:"external_source"`
54+
UserExternalId string `json:"external_id"`
55+
UserEmail string `json:"email"`
56+
OrgExternalId string `json:"external_org_id"`
57+
}
58+
59+
var request UserCreateRequest
60+
err := c.BindJSON(&request)
61+
if err != nil {
62+
log.Printf("Error binding JSON: %v", err)
63+
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error binding JSON"})
64+
return
65+
}
66+
67+
extUserId := request.UserExternalId
68+
extUserSource := request.UserExternalSource
69+
userEmail := request.UserEmail
70+
externalOrgId := request.OrgExternalId
71+
72+
org, err := models.DB.GetOrganisation(externalOrgId)
73+
if err != nil {
74+
log.Printf("Error retrieving org: %v", err)
75+
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error retrieving org"})
76+
}
77+
78+
// for now using email for username since we want to deprecate that field
79+
username := userEmail
80+
user, err := models.DB.CreateUser(userEmail, extUserSource, extUserId, org.ID, username)
81+
if err != nil {
82+
log.Printf("Error creating user: %v", err)
83+
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error creating user"})
84+
return
85+
}
86+
87+
c.JSON(200, gin.H{"status": "success", "user_id": user.ID})
88+
}

backend/middleware/webhook.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
"strings"
88
)
99

10-
func WebhookAuth() gin.HandlerFunc {
10+
func InternalApiAuth() gin.HandlerFunc {
1111
return func(c *gin.Context) {
1212
webhookSecret := os.Getenv("DIGGER_INTERNAL_SECRET")
1313
authHeader := c.Request.Header.Get("Authorization")

backend/migrations/20250220084846.sql

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- Modify "users" table
2+
ALTER TABLE "public"."users" ADD COLUMN "email" text NULL, ADD COLUMN "external_id" text NULL, ADD COLUMN "org_id" bigint NULL;

backend/migrations/20250220172054.sql

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- Modify "users" table
2+
ALTER TABLE "public"."users" ADD COLUMN "external_source" text NULL;

backend/migrations/20250220172321.sql

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- Create index "idx_user_external_source" to table: "users"
2+
CREATE UNIQUE INDEX "idx_user_external_source" ON "public"."users" ("external_source", "external_id");

backend/migrations/20250220173053.sql

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- Create index "idx_users_email" to table: "users"
2+
CREATE UNIQUE INDEX "idx_users_email" ON "public"."users" ("email");

backend/migrations/20250220173439.sql

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-- Rename a column from "org_id" to "organisation_id"
2+
ALTER TABLE "public"."users" RENAME COLUMN "org_id" TO "organisation_id";
3+
-- Modify "users" table
4+
ALTER TABLE "public"."users" ADD CONSTRAINT "fk_users_organisation" FOREIGN KEY ("organisation_id") REFERENCES "public"."organisations" ("id") ON UPDATE NO ACTION ON DELETE NO ACTION;

backend/migrations/20250221044813.sql

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-- Drop index "idx_organisation" from table: "organisations"
2+
DROP INDEX "public"."idx_organisation";
3+
-- Create index "idx_organisation" to table: "organisations"
4+
CREATE INDEX "idx_organisation" ON "public"."organisations" ("name");

backend/migrations/atlas.sum

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
h1:kHWmqYJMe9ZbdcztvM02SVEs5lyw/RFbKzZmqgbmSIk=
1+
h1:QQtQnP46bNFA5g3iD9R9tIJmWXQAue5yU7JTWucDhro=
22
20231227132525.sql h1:43xn7XC0GoJsCnXIMczGXWis9d504FAWi4F1gViTIcw=
33
20240115170600.sql h1:IW8fF/8vc40+eWqP/xDK+R4K9jHJ9QBSGO6rN9LtfSA=
44
20240116123649.sql h1:R1JlUIgxxF6Cyob9HdtMqiKmx/BfnsctTl5rvOqssQw=
@@ -35,3 +35,9 @@ h1:kHWmqYJMe9ZbdcztvM02SVEs5lyw/RFbKzZmqgbmSIk=
3535
20241107172343.sql h1:E1j+7R5TZlyCKEpyYmH1mJ2zh+y5hVbtQ/PuEMJR7us=
3636
20241114202249.sql h1:P2DhJK8MLe8gSAAz+Y5KNmsvKVw8KfLQPCncynYXEfM=
3737
20241229112312.sql h1:Fr06uwt7LcQoLh6bjGzKB+uy9i8+uk8m6jfi+OBBbP4=
38+
20250220084846.sql h1:bsZFw5QWJWSdxCwJ3rrYaSRQmlfGRsXY4ZUCIvKdJPE=
39+
20250220172054.sql h1:YIHVpr7DyCpR9sK8Hml4wsFzRbZzSfHqJ4/x+dOTnds=
40+
20250220172321.sql h1:Th+twkqGEQx3cXpAhNI+VoNmxhxsOfrtexObCVwWHXM=
41+
20250220173053.sql h1:Ry/j04tOPQceff4o/uPM0I1r5ltMcP2vuJc6/SQBcac=
42+
20250220173439.sql h1:Dho4Gw361D8kuL5pB56hUj5hZM1NPTbMYVBjBW54DkE=
43+
20250221044813.sql h1:PPcVcoMaMY5DXyoptQYeBOEwofrzIfyKcWRd3S12I2U=

backend/models/orgs.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77

88
type Organisation struct {
99
gorm.Model
10-
Name string `gorm:"uniqueIndex:idx_organisation"`
10+
Name string `gorm:"Index:idx_organisation"`
1111
ExternalSource string `gorm:"uniqueIndex:idx_external_source"`
1212
ExternalId string `gorm:"uniqueIndex:idx_external_source"`
1313
}

backend/models/storage.go

+17
Original file line numberDiff line numberDiff line change
@@ -1032,6 +1032,23 @@ func (db *Database) GetOrganisation(tenantId any) (*Organisation, error) {
10321032
return org, nil
10331033
}
10341034

1035+
func (db *Database) CreateUser(email string, externalSource string, externalId string, orgId uint, username string) (*User, error) {
1036+
user := &User{
1037+
Email: email,
1038+
ExternalId: externalId,
1039+
ExternalSource: externalSource,
1040+
OrganisationId: &orgId,
1041+
Username: username,
1042+
}
1043+
result := db.GormDB.Save(user)
1044+
if result.Error != nil {
1045+
log.Printf("Failed to create user: %v, error: %v\n", externalId, result.Error)
1046+
return nil, result.Error
1047+
}
1048+
log.Printf("User (id: %v) has been created successfully\n", externalId)
1049+
return user, nil
1050+
}
1051+
10351052
func (db *Database) CreateOrganisation(name string, externalSource string, tenantId string) (*Organisation, error) {
10361053
org := &Organisation{Name: name, ExternalSource: externalSource, ExternalId: tenantId}
10371054
result := db.GormDB.Save(org)

backend/models/user.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,11 @@ import "gorm.io/gorm"
44

55
type User struct {
66
gorm.Model
7-
Username string `gorm:"uniqueIndex:idx_user"`
7+
Email string `gorm:"uniqueIndex"`
8+
ExternalSource string `gorm:"uniqueIndex:idx_user_external_source"`
9+
ExternalId string `gorm:"uniqueIndex:idx_user_external_source"`
10+
// the default org currently in use by this user
11+
OrganisationId *uint
12+
Organisation Organisation
13+
Username string `gorm:"uniqueIndex:idx_user"`
814
}

0 commit comments

Comments
 (0)