Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion cmd/http/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"log"

"github.com/joho/godotenv"
_ "github.com/pangolin-do-golang/thumb-processor-api/docs"
dbAdapter "github.com/pangolin-do-golang/thumb-processor-api/internal/adapters/db"
"github.com/pangolin-do-golang/thumb-processor-api/internal/adapters/rest/server"
Expand All @@ -20,6 +21,10 @@ import (
// @host localhost:8080
// @BasePath /
func main() {
if err := godotenv.Load(); err != nil {
log.Fatalln(err)
}

cfg, err := config.Load()
if err != nil {
log.Fatalln(err)
Expand All @@ -42,7 +47,7 @@ func main() {
ThumService: thumbService,
})

restServer.Serve()
restServer.Serve(cfg)
}

func newDatabaseConnection(cfg *config.Config) (*gorm.DB, error) {
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ services:
image: postgres:16.2
restart: always
ports:
- "5432:5432"
- "${DB_PORT-5432}:5432"
environment:
- POSTGRES_USER=${DB_USERNAME}
- POSTGRES_PASSWORD=${DB_PASSWORD}
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ require (
github.com/caarlos0/env/v11 v11.3.1
github.com/gin-gonic/gin v1.10.0
github.com/google/uuid v1.6.0
github.com/joho/godotenv v1.5.1
github.com/stretchr/testify v1.10.0
github.com/swaggo/files v1.0.1
github.com/swaggo/gin-swagger v1.6.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
Expand Down
7 changes: 7 additions & 0 deletions internal/adapters/db/error_sentinels.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package db

import "errors"

var (
RequiredUserIDError = errors.New("logged user ID is required")
)
2 changes: 2 additions & 0 deletions internal/adapters/db/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package db

import (
"database/sql"

"gorm.io/gorm"
)

Expand All @@ -19,6 +20,7 @@ type IDB interface {
Last(dest interface{}, conds ...interface{}) (tx *gorm.DB)
Find(dest interface{}, conds ...interface{}) (tx *gorm.DB)
Update(column string, value interface{}) (tx *gorm.DB)
Updates(values interface{}) (tx *gorm.DB)
Delete(value interface{}, conds ...interface{}) (tx *gorm.DB)
Count(count *int64) (tx *gorm.DB)
Row() *sql.Row
Expand Down
38 changes: 30 additions & 8 deletions internal/adapters/db/thumb_repository.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package db

import (
"context"
"errors"

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

type ThumbPostgres struct {
BaseModel
UserID int
VideoPath string
Status string
Error string
Expand All @@ -23,11 +25,14 @@ type PostgresThumbRepository struct {
db IDB
}

func (r *PostgresThumbRepository) Create(process *entity.ThumbProcess) error {
func (r *PostgresThumbRepository) Create(ctx context.Context, process *entity.ThumbProcess) error {
userID, err := r.getUserID(ctx)
if err != nil {
return err
}

record := &ThumbPostgres{
BaseModel: BaseModel{
ID: process.ID,
},
UserID: userID,
VideoPath: process.Video.Path,
ThumbnailPath: process.Thumbnail.Path,
Status: process.Status,
Expand All @@ -36,14 +41,22 @@ func (r *PostgresThumbRepository) Create(process *entity.ThumbProcess) error {

result := r.db.Create(record)

process.ID = record.ID

return result.Error
}

func (r *PostgresThumbRepository) List() *[]entity.ThumbProcess {
func (r *PostgresThumbRepository) List(ctx context.Context) *[]entity.ThumbProcess {
processes := []entity.ThumbProcess{}

userID, err := r.getUserID(ctx)
if err != nil {
return &processes
}

records := []ThumbPostgres{}

r.db.Find(&records)
r.db.Find(&records, "user_id = ?", userID)

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

func (r *PostgresThumbRepository) Update(process *entity.ThumbProcess) (*entity.ThumbProcess, error) {
func (r *PostgresThumbRepository) Update(ctx context.Context, process *entity.ThumbProcess) (*entity.ThumbProcess, error) {
if process.ID == uuid.Nil {
return nil, errors.New("process id is required")
}

result := r.db.Save(&ThumbPostgres{
result := r.db.Updates(&ThumbPostgres{
BaseModel: BaseModel{
ID: process.ID,
},
Expand All @@ -97,3 +110,12 @@ func (r *PostgresThumbRepository) Update(process *entity.ThumbProcess) (*entity.
},
}, nil
}

func (r *PostgresThumbRepository) getUserID(ctx context.Context) (int, error) {
userID, ok := ctx.Value("logged_user_id").(int)
if !ok {
return 0, RequiredUserIDError
}

return userID, nil
}
68 changes: 50 additions & 18 deletions internal/adapters/db/thumb_repository_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
package db

import (
"context"
"errors"
"testing"

Expand All @@ -14,8 +15,7 @@ import (
)

func TestPostgresThumbRepository_Create(t *testing.T) {
mockDB := new(adaptermocks.IDB)
repo := NewPostgresThumbRepository(mockDB)
mockDB, repo, _, ctx := setupTest()

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

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

err := repo.Create(process)
err := repo.Create(ctx, process)

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

err := repo.Create(process)
err := repo.Create(ctx, process)

assert.Error(t, err)
assert.Equal(t, expectedError, err)
})

t.Run("create with missing user ID", func(t *testing.T) {
process := &entity.ThumbProcess{
Video: entity.ThumbProcessVideo{
Path: "test-video.mp4",
},
Thumbnail: entity.ThumbProcessThumb{
Path: "test-thumb.jpg",
},
Status: "pending",
Error: "",
}

err := repo.Create(context.Background(), process)

assert.Error(t, err)
assert.Equal(t, RequiredUserIDError, err)
})
}

func TestPostgresThumbRepository_List(t *testing.T) {
mockDB := new(adaptermocks.IDB)
repo := NewPostgresThumbRepository(mockDB)
mockDB, repo, mockedUserID, ctx := setupTest()

t.Run("successful list retrieval", func(t *testing.T) {
mockDB.On("Find", mock.AnythingOfType("*[]db.ThumbPostgres"), mock.Anything).
mockDB.On("Find", mock.AnythingOfType("*[]db.ThumbPostgres"), "user_id = ?", mockedUserID).
Run(func(args mock.Arguments) {
arg := args.Get(0).(*[]ThumbPostgres)
*arg = []ThumbPostgres{
{
BaseModel: BaseModel{
ID: uuid.New(),
},
UserID: 1,
VideoPath: "video1.mp4",
ThumbnailPath: "thumb1.jpg",
Status: "completed",
Expand All @@ -80,6 +98,7 @@ func TestPostgresThumbRepository_List(t *testing.T) {
BaseModel: BaseModel{
ID: uuid.New(),
},
UserID: 1,
VideoPath: "video2.mp4",
ThumbnailPath: "thumb2.jpg",
Status: "pending",
Expand All @@ -88,7 +107,7 @@ func TestPostgresThumbRepository_List(t *testing.T) {
}
}).Return(&gorm.DB{}).Once()

processes := *repo.List()
processes := *repo.List(ctx)

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

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

processes := repo.List()
processes := repo.List(ctx)

assert.Empty(t, processes)
})

t.Run("list with missing user ID", func(t *testing.T) {
processes := repo.List(context.Background())
assert.Empty(t, processes)
})
}

func TestPostgresThumbRepository_Update(t *testing.T) {
mockDB := new(adaptermocks.IDB)
repo := NewPostgresThumbRepository(mockDB)
mockDB, repo, _, ctx := setupTest()

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

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

updated, err := repo.Update(process)
updated, err := repo.Update(ctx, process)

assert.NoError(t, err)
assert.NotNil(t, updated)
Expand All @@ -150,12 +173,11 @@ func TestPostgresThumbRepository_Update(t *testing.T) {
Error: "",
}

updated, err := repo.Update(process)
updated, err := repo.Update(ctx, process)

assert.Nil(t, updated)
assert.Error(t, err)
assert.Equal(t, "process id is required", err.Error())
mockDB.AssertNotCalled(t, "Save")
})

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

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

updated, err := repo.Update(process)
updated, err := repo.Update(ctx, process)

assert.Nil(t, updated)
assert.Error(t, err)
Expand All @@ -195,3 +217,13 @@ func TestThumbPostgres_TableName(t *testing.T) {
thumb := ThumbPostgres{}
assert.Equal(t, "thumb", thumb.TableName())
}

func setupTest() (*adaptermocks.IDB, *PostgresThumbRepository, int, context.Context) {
mockedUserID := 1
mockDB := new(adaptermocks.IDB)

return mockDB,
NewPostgresThumbRepository(mockDB),
mockedUserID,
context.WithValue(context.Background(), "logged_user_id", mockedUserID)
}
19 changes: 11 additions & 8 deletions internal/adapters/rest/handler/thumb_handler.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package handler

import (
"net/http"

"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/pangolin-do-golang/thumb-processor-api/internal/adapters/rest/middleware"
"github.com/pangolin-do-golang/thumb-processor-api/internal/core/ports"
"github.com/pangolin-do-golang/thumb-processor-api/internal/core/thumb"
"github.com/pangolin-do-golang/thumb-processor-api/internal/core/users"
"net/http"
)

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

func (h *ThumbHandler) RegisterRoutes(router *gin.RouterGroup) {
thumbGroup := router.Group("/thumbs")
thumbGroup.POST("", middleware.AuthMiddleware(users.GetAllowedUsers), h.CreateProcess)
thumbGroup.POST("", h.CreateProcess)
thumbGroup.GET("", h.ListProcesses)
}

func (h *ThumbHandler) RegisterInternalRoutes(router *gin.Engine) {
thumbGroup := router.Group("/thumbs")
thumbGroup.PUT("/:id", h.UpdateProcess)
thumbGroup.GET("", middleware.AuthMiddleware(users.GetAllowedUsers), h.ListProcesses)
}

// @Summary Create a new thumbnail process
Expand Down Expand Up @@ -58,7 +61,7 @@ func (h *ThumbHandler) CreateProcess(c *gin.Context) {
if ok {
userEmail = ctxUser.(string)
}
err := h.thumbService.CreateProcessAsync(&ports.CreateProcessRequest{
err := h.thumbService.CreateProcessAsync(c.Request.Context(), &ports.CreateProcessRequest{
UserEmail: userEmail,
Url: request.URL,
})
Expand Down Expand Up @@ -103,7 +106,7 @@ func (h *ThumbHandler) UpdateProcess(c *gin.Context) {
return
}

updated, err := h.thumbService.UpdateProcess(&ports.UpdateProcessRequest{
updated, err := h.thumbService.UpdateProcess(c.Request.Context(), &ports.UpdateProcessRequest{
ID: id,
Status: request.Status,
Error: request.Error,
Expand Down Expand Up @@ -133,7 +136,7 @@ func (h *ThumbHandler) UpdateProcess(c *gin.Context) {
// @Failure 500 {object} ErrorResponse
// @Router /thumbs [get]
func (h *ThumbHandler) ListProcesses(c *gin.Context) {
processes := h.thumbService.ListProcess()
processes := h.thumbService.ListProcess(c.Request.Context())

response := make([]ThumbProcessResponse, len(*processes))
for i, process := range *processes {
Expand Down
Loading
Loading