Skip to content

Commit 956ebe6

Browse files
authored
Merge pull request #50 from Arjuna-Ragil/feat/48-dynamic-ddl
feat: backend table DDL logic added
2 parents 92d2789 + adbff8d commit 956ebe6

5 files changed

Lines changed: 143 additions & 12 deletions

File tree

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package repository
2+
3+
type DynamicDBRepo struct {
4+
DB *DBContainer
5+
}
6+
7+
func NewDynamicDB(db *DBContainer) *DynamicDBRepo {
8+
return &DynamicDBRepo{DB: db}
9+
}
10+
11+
func (dr *DynamicDBRepo) CreateDynamicDB(query string) error {
12+
_, err := dr.DB.Sqlx.Exec(query)
13+
if err != nil {
14+
return err
15+
}
16+
return nil
17+
}

Internal/api/Routes/routev1.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@ import (
99
)
1010

1111
type Deps struct {
12-
User *handlers.UserHandler
13-
Auth *handlers.AuthHandler
14-
System *handlers.SystemHandler
15-
Project *handlers.ProjectHandler
16-
UserRepo *repository.UserRepository
17-
Config *config.Config
12+
User *handlers.UserHandler
13+
Auth *handlers.AuthHandler
14+
System *handlers.SystemHandler
15+
Project *handlers.ProjectHandler
16+
DynamicDB *handlers.DynamicDBHandler
17+
UserRepo *repository.UserRepository
18+
Config *config.Config
1819
}
1920

2021
func SetupRouterV1(r *gin.Engine, deps Deps) {
@@ -57,6 +58,7 @@ func SetupRouterV1(r *gin.Engine, deps Deps) {
5758
{
5859
manage.POST("/invite/:projectid", deps.Project.InviteProjectHandler)
5960
manage.DELETE("/remove/:projuserid", deps.Project.RemoveProjectUserHandler)
61+
manage.POST("/create/table/:projectid", deps.DynamicDB.CreateDynamicDBHandler)
6062
}
6163
}
6264
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package handlers
2+
3+
import (
4+
"strconv"
5+
6+
"github.com/Arjuna-Ragil/Localbase/Internal/core/services"
7+
"github.com/gin-gonic/gin"
8+
)
9+
10+
type DynamicDBHandler struct {
11+
Serv *services.DynamicDBService
12+
}
13+
14+
func NewDynamicDBHandler(serv *services.DynamicDBService) *DynamicDBHandler {
15+
return &DynamicDBHandler{Serv: serv}
16+
}
17+
18+
func (dh *DynamicDBHandler) CreateDynamicDBHandler(c *gin.Context) {
19+
projectIDStr := c.Param("projectid")
20+
projectID, err := strconv.Atoi(projectIDStr)
21+
if err != nil {
22+
c.JSON(400, gin.H{
23+
"message": "Project ID not valid",
24+
"error": err.Error(),
25+
})
26+
return
27+
}
28+
var input services.CreateTableReq
29+
if err := c.ShouldBindJSON(&input); err != nil {
30+
c.JSON(400, gin.H{
31+
"message": "Invalid json body",
32+
"error": err.Error(),
33+
})
34+
return
35+
}
36+
if err := dh.Serv.CreateDynamicTable(uint(projectID), input); nil != err {
37+
c.JSON(500, gin.H{
38+
"message": "Failed to create dynamic table",
39+
"error": err.Error(),
40+
})
41+
return
42+
}
43+
c.JSON(200, gin.H{
44+
"message": "Successfully created dynamic table",
45+
"data": nil,
46+
})
47+
48+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package services
2+
3+
import (
4+
"fmt"
5+
"regexp"
6+
"strings"
7+
8+
"github.com/Arjuna-Ragil/Localbase/Internal/adapters/repository"
9+
)
10+
11+
type DynamicDBService struct {
12+
Repo *repository.DynamicDBRepo
13+
}
14+
15+
func NewDynamicDBService(repo *repository.DynamicDBRepo) *DynamicDBService {
16+
return &DynamicDBService{Repo: repo}
17+
}
18+
19+
type ColumnReq struct {
20+
Name string `json:"name"`
21+
Type string `json:"type"`
22+
}
23+
24+
type CreateTableReq struct {
25+
TableName string `json:"table_name"`
26+
Columns []ColumnReq `json:"columns"`
27+
}
28+
29+
func sanitizeInput(input string) string {
30+
result := strings.ToLower(input)
31+
result = strings.ReplaceAll(result, " ", "_")
32+
reg := regexp.MustCompile("[^a-z0-9_]+")
33+
result = reg.ReplaceAllString(result, "")
34+
return result
35+
}
36+
37+
func (ds *DynamicDBService) CreateDynamicTable(projectID uint, req CreateTableReq) error {
38+
safeTableName := fmt.Sprintf("proj_%d_%s", projectID, sanitizeInput(req.TableName))
39+
40+
var queryBuilder strings.Builder
41+
queryBuilder.WriteString(fmt.Sprintf("CREATE TABLE %s (", safeTableName))
42+
queryBuilder.WriteString("id SERIAL PRIMARY KEY, ")
43+
queryBuilder.WriteString("created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, ")
44+
45+
for _, col := range req.Columns {
46+
safeColName := sanitizeInput(col.Name)
47+
queryBuilder.WriteString(fmt.Sprintf("%s %s, ", safeColName, col.Type))
48+
}
49+
50+
query := queryBuilder.String()
51+
query = strings.TrimSuffix(query, ", ")
52+
query += ");"
53+
54+
fmt.Println(query)
55+
if err := ds.Repo.CreateDynamicDB(query); err != nil {
56+
return err
57+
}
58+
return nil
59+
}

cmd/server/main.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,17 @@ func SetupApp(db *repository.DBContainer, cfg *config.Config) Routes.Deps {
6464
projectService := services.NewProjectService(projectRepo, userRepo)
6565
projectHandler := handlers.NewProjectHandler(projectService)
6666

67+
dynamicDBRepo := repository.NewDynamicDB(db)
68+
dynamicDBService := services.NewDynamicDBService(dynamicDBRepo)
69+
dynamicDBHandler := handlers.NewDynamicDBHandler(dynamicDBService)
70+
6771
return Routes.Deps{
68-
User: userHandler,
69-
Auth: authHandler,
70-
System: systemHandler,
71-
Project: projectHandler,
72-
UserRepo: userRepo,
73-
Config: cfg,
72+
User: userHandler,
73+
Auth: authHandler,
74+
System: systemHandler,
75+
Project: projectHandler,
76+
DynamicDB: dynamicDBHandler,
77+
UserRepo: userRepo,
78+
Config: cfg,
7479
}
7580
}

0 commit comments

Comments
 (0)