Skip to content

Commit ae2600b

Browse files
Refactor: apply user id context on repositories (#7)
* refactor: apply user id context on repositories * fix: config tests
1 parent 73e9790 commit ae2600b

22 files changed

+262
-127
lines changed

cmd/http/main.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package main
33
import (
44
"log"
55

6+
"github.com/joho/godotenv"
67
_ "github.com/pangolin-do-golang/thumb-processor-api/docs"
78
dbAdapter "github.com/pangolin-do-golang/thumb-processor-api/internal/adapters/db"
89
"github.com/pangolin-do-golang/thumb-processor-api/internal/adapters/rest/server"
@@ -20,6 +21,10 @@ import (
2021
// @host localhost:8080
2122
// @BasePath /
2223
func main() {
24+
if err := godotenv.Load(); err != nil {
25+
log.Fatalln(err)
26+
}
27+
2328
cfg, err := config.Load()
2429
if err != nil {
2530
log.Fatalln(err)
@@ -42,7 +47,7 @@ func main() {
4247
ThumService: thumbService,
4348
})
4449

45-
restServer.Serve()
50+
restServer.Serve(cfg)
4651
}
4752

4853
func newDatabaseConnection(cfg *config.Config) (*gorm.DB, error) {

docker-compose.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ services:
55
image: postgres:16.2
66
restart: always
77
ports:
8-
- "5432:5432"
8+
- "${DB_PORT-5432}:5432"
99
environment:
1010
- POSTGRES_USER=${DB_USERNAME}
1111
- POSTGRES_PASSWORD=${DB_PASSWORD}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ require (
99
github.com/caarlos0/env/v11 v11.3.1
1010
github.com/gin-gonic/gin v1.10.0
1111
github.com/google/uuid v1.6.0
12+
github.com/joho/godotenv v1.5.1
1213
github.com/stretchr/testify v1.10.0
1314
github.com/swaggo/files v1.0.1
1415
github.com/swaggo/gin-swagger v1.6.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
8484
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
8585
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
8686
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
87+
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
88+
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
8789
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
8890
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
8991
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package db
2+
3+
import "errors"
4+
5+
var (
6+
RequiredUserIDError = errors.New("logged user ID is required")
7+
)

internal/adapters/db/interface.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package db
22

33
import (
44
"database/sql"
5+
56
"gorm.io/gorm"
67
)
78

@@ -19,6 +20,7 @@ type IDB interface {
1920
Last(dest interface{}, conds ...interface{}) (tx *gorm.DB)
2021
Find(dest interface{}, conds ...interface{}) (tx *gorm.DB)
2122
Update(column string, value interface{}) (tx *gorm.DB)
23+
Updates(values interface{}) (tx *gorm.DB)
2224
Delete(value interface{}, conds ...interface{}) (tx *gorm.DB)
2325
Count(count *int64) (tx *gorm.DB)
2426
Row() *sql.Row

internal/adapters/db/thumb_repository.go

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package db
22

33
import (
4+
"context"
45
"errors"
56

67
"github.com/google/uuid"
@@ -9,6 +10,7 @@ import (
910

1011
type ThumbPostgres struct {
1112
BaseModel
13+
UserID int
1214
VideoPath string
1315
Status string
1416
Error string
@@ -23,11 +25,14 @@ type PostgresThumbRepository struct {
2325
db IDB
2426
}
2527

26-
func (r *PostgresThumbRepository) Create(process *entity.ThumbProcess) error {
28+
func (r *PostgresThumbRepository) Create(ctx context.Context, process *entity.ThumbProcess) error {
29+
userID, err := r.getUserID(ctx)
30+
if err != nil {
31+
return err
32+
}
33+
2734
record := &ThumbPostgres{
28-
BaseModel: BaseModel{
29-
ID: process.ID,
30-
},
35+
UserID: userID,
3136
VideoPath: process.Video.Path,
3237
ThumbnailPath: process.Thumbnail.Path,
3338
Status: process.Status,
@@ -36,14 +41,22 @@ func (r *PostgresThumbRepository) Create(process *entity.ThumbProcess) error {
3641

3742
result := r.db.Create(record)
3843

44+
process.ID = record.ID
45+
3946
return result.Error
4047
}
4148

42-
func (r *PostgresThumbRepository) List() *[]entity.ThumbProcess {
49+
func (r *PostgresThumbRepository) List(ctx context.Context) *[]entity.ThumbProcess {
4350
processes := []entity.ThumbProcess{}
51+
52+
userID, err := r.getUserID(ctx)
53+
if err != nil {
54+
return &processes
55+
}
56+
4457
records := []ThumbPostgres{}
4558

46-
r.db.Find(&records)
59+
r.db.Find(&records, "user_id = ?", userID)
4760

4861
for _, record := range records {
4962
processes = append(processes, entity.ThumbProcess{
@@ -66,12 +79,12 @@ func NewPostgresThumbRepository(db IDB) *PostgresThumbRepository {
6679
return &PostgresThumbRepository{db: db}
6780
}
6881

69-
func (r *PostgresThumbRepository) Update(process *entity.ThumbProcess) (*entity.ThumbProcess, error) {
82+
func (r *PostgresThumbRepository) Update(ctx context.Context, process *entity.ThumbProcess) (*entity.ThumbProcess, error) {
7083
if process.ID == uuid.Nil {
7184
return nil, errors.New("process id is required")
7285
}
7386

74-
result := r.db.Save(&ThumbPostgres{
87+
result := r.db.Updates(&ThumbPostgres{
7588
BaseModel: BaseModel{
7689
ID: process.ID,
7790
},
@@ -97,3 +110,12 @@ func (r *PostgresThumbRepository) Update(process *entity.ThumbProcess) (*entity.
97110
},
98111
}, nil
99112
}
113+
114+
func (r *PostgresThumbRepository) getUserID(ctx context.Context) (int, error) {
115+
userID, ok := ctx.Value("logged_user_id").(int)
116+
if !ok {
117+
return 0, RequiredUserIDError
118+
}
119+
120+
return userID, nil
121+
}

internal/adapters/db/thumb_repository_test.go

Lines changed: 50 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
package db
33

44
import (
5+
"context"
56
"errors"
67
"testing"
78

@@ -14,8 +15,7 @@ import (
1415
)
1516

1617
func TestPostgresThumbRepository_Create(t *testing.T) {
17-
mockDB := new(adaptermocks.IDB)
18-
repo := NewPostgresThumbRepository(mockDB)
18+
mockDB, repo, _, ctx := setupTest()
1919

2020
t.Run("successful creation", func(t *testing.T) {
2121
process := &entity.ThumbProcess{
@@ -31,7 +31,7 @@ func TestPostgresThumbRepository_Create(t *testing.T) {
3131

3232
mockDB.On("Create", mock.AnythingOfType("*db.ThumbPostgres")).Return(&gorm.DB{}).Once()
3333

34-
err := repo.Create(process)
34+
err := repo.Create(ctx, process)
3535

3636
assert.NoError(t, err)
3737
})
@@ -51,26 +51,44 @@ func TestPostgresThumbRepository_Create(t *testing.T) {
5151
expectedError := errors.New("database error")
5252
mockDB.On("Create", mock.AnythingOfType("*db.ThumbPostgres")).Return(&gorm.DB{Error: expectedError}).Once()
5353

54-
err := repo.Create(process)
54+
err := repo.Create(ctx, process)
5555

5656
assert.Error(t, err)
5757
assert.Equal(t, expectedError, err)
5858
})
59+
60+
t.Run("create with missing user ID", func(t *testing.T) {
61+
process := &entity.ThumbProcess{
62+
Video: entity.ThumbProcessVideo{
63+
Path: "test-video.mp4",
64+
},
65+
Thumbnail: entity.ThumbProcessThumb{
66+
Path: "test-thumb.jpg",
67+
},
68+
Status: "pending",
69+
Error: "",
70+
}
71+
72+
err := repo.Create(context.Background(), process)
73+
74+
assert.Error(t, err)
75+
assert.Equal(t, RequiredUserIDError, err)
76+
})
5977
}
6078

6179
func TestPostgresThumbRepository_List(t *testing.T) {
62-
mockDB := new(adaptermocks.IDB)
63-
repo := NewPostgresThumbRepository(mockDB)
80+
mockDB, repo, mockedUserID, ctx := setupTest()
6481

6582
t.Run("successful list retrieval", func(t *testing.T) {
66-
mockDB.On("Find", mock.AnythingOfType("*[]db.ThumbPostgres"), mock.Anything).
83+
mockDB.On("Find", mock.AnythingOfType("*[]db.ThumbPostgres"), "user_id = ?", mockedUserID).
6784
Run(func(args mock.Arguments) {
6885
arg := args.Get(0).(*[]ThumbPostgres)
6986
*arg = []ThumbPostgres{
7087
{
7188
BaseModel: BaseModel{
7289
ID: uuid.New(),
7390
},
91+
UserID: 1,
7492
VideoPath: "video1.mp4",
7593
ThumbnailPath: "thumb1.jpg",
7694
Status: "completed",
@@ -80,6 +98,7 @@ func TestPostgresThumbRepository_List(t *testing.T) {
8098
BaseModel: BaseModel{
8199
ID: uuid.New(),
82100
},
101+
UserID: 1,
83102
VideoPath: "video2.mp4",
84103
ThumbnailPath: "thumb2.jpg",
85104
Status: "pending",
@@ -88,7 +107,7 @@ func TestPostgresThumbRepository_List(t *testing.T) {
88107
}
89108
}).Return(&gorm.DB{}).Once()
90109

91-
processes := *repo.List()
110+
processes := *repo.List(ctx)
92111

93112
assert.Len(t, processes, 2)
94113
assert.Equal(t, "video1.mp4", processes[0].Video.Path)
@@ -100,21 +119,25 @@ func TestPostgresThumbRepository_List(t *testing.T) {
100119
})
101120

102121
t.Run("empty list", func(t *testing.T) {
103-
mockDB.On("Find", mock.AnythingOfType("*[]db.ThumbPostgres"), mock.Anything).
122+
mockDB.On("Find", mock.AnythingOfType("*[]db.ThumbPostgres"), "user_id = ?", mockedUserID).
104123
Run(func(args mock.Arguments) {
105124
arg := args.Get(0).(*[]ThumbPostgres)
106125
*arg = []ThumbPostgres{}
107126
}).Return(&gorm.DB{}).Once()
108127

109-
processes := repo.List()
128+
processes := repo.List(ctx)
129+
130+
assert.Empty(t, processes)
131+
})
110132

133+
t.Run("list with missing user ID", func(t *testing.T) {
134+
processes := repo.List(context.Background())
111135
assert.Empty(t, processes)
112136
})
113137
}
114138

115139
func TestPostgresThumbRepository_Update(t *testing.T) {
116-
mockDB := new(adaptermocks.IDB)
117-
repo := NewPostgresThumbRepository(mockDB)
140+
mockDB, repo, _, ctx := setupTest()
118141

119142
t.Run("successful update", func(t *testing.T) {
120143
processID := uuid.New()
@@ -130,9 +153,9 @@ func TestPostgresThumbRepository_Update(t *testing.T) {
130153
Error: "",
131154
}
132155

133-
mockDB.On("Save", mock.AnythingOfType("*db.ThumbPostgres")).Return(&gorm.DB{}).Once()
156+
mockDB.On("Updates", mock.AnythingOfType("*db.ThumbPostgres")).Return(&gorm.DB{}).Once()
134157

135-
updated, err := repo.Update(process)
158+
updated, err := repo.Update(ctx, process)
136159

137160
assert.NoError(t, err)
138161
assert.NotNil(t, updated)
@@ -150,12 +173,11 @@ func TestPostgresThumbRepository_Update(t *testing.T) {
150173
Error: "",
151174
}
152175

153-
updated, err := repo.Update(process)
176+
updated, err := repo.Update(ctx, process)
154177

155178
assert.Nil(t, updated)
156179
assert.Error(t, err)
157180
assert.Equal(t, "process id is required", err.Error())
158-
mockDB.AssertNotCalled(t, "Save")
159181
})
160182

161183
t.Run("database error during update", func(t *testing.T) {
@@ -173,9 +195,9 @@ func TestPostgresThumbRepository_Update(t *testing.T) {
173195
}
174196

175197
expectedError := errors.New("database error")
176-
mockDB.On("Save", mock.AnythingOfType("*db.ThumbPostgres")).Return(&gorm.DB{Error: expectedError}).Once()
198+
mockDB.On("Updates", mock.AnythingOfType("*db.ThumbPostgres")).Return(&gorm.DB{Error: expectedError}).Once()
177199

178-
updated, err := repo.Update(process)
200+
updated, err := repo.Update(ctx, process)
179201

180202
assert.Nil(t, updated)
181203
assert.Error(t, err)
@@ -195,3 +217,13 @@ func TestThumbPostgres_TableName(t *testing.T) {
195217
thumb := ThumbPostgres{}
196218
assert.Equal(t, "thumb", thumb.TableName())
197219
}
220+
221+
func setupTest() (*adaptermocks.IDB, *PostgresThumbRepository, int, context.Context) {
222+
mockedUserID := 1
223+
mockDB := new(adaptermocks.IDB)
224+
225+
return mockDB,
226+
NewPostgresThumbRepository(mockDB),
227+
mockedUserID,
228+
context.WithValue(context.Background(), "logged_user_id", mockedUserID)
229+
}

internal/adapters/rest/handler/thumb_handler.go

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
package handler
22

33
import (
4+
"net/http"
5+
46
"github.com/gin-gonic/gin"
57
"github.com/google/uuid"
6-
"github.com/pangolin-do-golang/thumb-processor-api/internal/adapters/rest/middleware"
78
"github.com/pangolin-do-golang/thumb-processor-api/internal/core/ports"
89
"github.com/pangolin-do-golang/thumb-processor-api/internal/core/thumb"
9-
"github.com/pangolin-do-golang/thumb-processor-api/internal/core/users"
10-
"net/http"
1110
)
1211

1312
type ThumbHandler struct {
@@ -22,9 +21,13 @@ func NewThumbHandler(service thumb.IThumbService) *ThumbHandler {
2221

2322
func (h *ThumbHandler) RegisterRoutes(router *gin.RouterGroup) {
2423
thumbGroup := router.Group("/thumbs")
25-
thumbGroup.POST("", middleware.AuthMiddleware(users.GetAllowedUsers), h.CreateProcess)
24+
thumbGroup.POST("", h.CreateProcess)
25+
thumbGroup.GET("", h.ListProcesses)
26+
}
27+
28+
func (h *ThumbHandler) RegisterInternalRoutes(router *gin.Engine) {
29+
thumbGroup := router.Group("/thumbs")
2630
thumbGroup.PUT("/:id", h.UpdateProcess)
27-
thumbGroup.GET("", middleware.AuthMiddleware(users.GetAllowedUsers), h.ListProcesses)
2831
}
2932

3033
// @Summary Create a new thumbnail process
@@ -58,7 +61,7 @@ func (h *ThumbHandler) CreateProcess(c *gin.Context) {
5861
if ok {
5962
userEmail = ctxUser.(string)
6063
}
61-
err := h.thumbService.CreateProcessAsync(&ports.CreateProcessRequest{
64+
err := h.thumbService.CreateProcessAsync(c.Request.Context(), &ports.CreateProcessRequest{
6265
UserEmail: userEmail,
6366
Url: request.URL,
6467
})
@@ -103,7 +106,7 @@ func (h *ThumbHandler) UpdateProcess(c *gin.Context) {
103106
return
104107
}
105108

106-
updated, err := h.thumbService.UpdateProcess(&ports.UpdateProcessRequest{
109+
updated, err := h.thumbService.UpdateProcess(c.Request.Context(), &ports.UpdateProcessRequest{
107110
ID: id,
108111
Status: request.Status,
109112
Error: request.Error,
@@ -133,7 +136,7 @@ func (h *ThumbHandler) UpdateProcess(c *gin.Context) {
133136
// @Failure 500 {object} ErrorResponse
134137
// @Router /thumbs [get]
135138
func (h *ThumbHandler) ListProcesses(c *gin.Context) {
136-
processes := h.thumbService.ListProcess()
139+
processes := h.thumbService.ListProcess(c.Request.Context())
137140

138141
response := make([]ThumbProcessResponse, len(*processes))
139142
for i, process := range *processes {

0 commit comments

Comments
 (0)