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
44 changes: 24 additions & 20 deletions internal/repositories/postgres/lifecycle_repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,27 +23,29 @@ func NewLifecycleRepository(db DB) *LifecycleRepository {
}

func (r *LifecycleRepository) Create(ctx context.Context, rule *domain.LifecycleRule) error {
tenantID := appcontext.TenantIDFromContext(ctx)
query := `
INSERT INTO lifecycle_rules (id, user_id, bucket_name, prefix, expiration_days, enabled, created_at, updated_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
INSERT INTO lifecycle_rules (id, user_id, tenant_id, bucket_name, prefix, expiration_days, enabled, created_at, updated_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
`
_, err := r.db.Exec(ctx, query, rule.ID, rule.UserID, rule.BucketName, rule.Prefix, rule.ExpirationDays, rule.Enabled, rule.CreatedAt, rule.UpdatedAt)
_, err := r.db.Exec(ctx, query, rule.ID, rule.UserID, tenantID, rule.BucketName, rule.Prefix, rule.ExpirationDays, rule.Enabled, rule.CreatedAt, rule.UpdatedAt)
if err != nil {
return errors.Wrap(errors.Internal, "failed to create lifecycle rule", err)
}
return nil
}

func (r *LifecycleRepository) Get(ctx context.Context, id uuid.UUID) (*domain.LifecycleRule, error) {
userId := appcontext.UserIDFromContext(ctx)
userID := appcontext.UserIDFromContext(ctx)
tenantID := appcontext.TenantIDFromContext(ctx)
query := `
SELECT id, user_id, bucket_name, prefix, expiration_days, enabled, created_at, updated_at
SELECT id, user_id, tenant_id, bucket_name, prefix, expiration_days, enabled, created_at, updated_at
FROM lifecycle_rules
WHERE id = $1 AND user_id = $2
WHERE id = $1 AND user_id = $2 AND tenant_id = $3
`
var rule domain.LifecycleRule
err := r.db.QueryRow(ctx, query, id, userId).Scan(
&rule.ID, &rule.UserID, &rule.BucketName, &rule.Prefix, &rule.ExpirationDays, &rule.Enabled, &rule.CreatedAt, &rule.UpdatedAt,
err := r.db.QueryRow(ctx, query, id, userID, tenantID).Scan(
&rule.ID, &rule.UserID, &rule.TenantID, &rule.BucketName, &rule.Prefix, &rule.ExpirationDays, &rule.Enabled, &rule.CreatedAt, &rule.UpdatedAt,
)
if err != nil {
if stdlib_errors.Is(err, pgx.ErrNoRows) {
Expand All @@ -55,23 +57,24 @@ func (r *LifecycleRepository) Get(ctx context.Context, id uuid.UUID) (*domain.Li
}

func (r *LifecycleRepository) List(ctx context.Context, bucketName string) ([]*domain.LifecycleRule, error) {
userId := appcontext.UserIDFromContext(ctx)
userID := appcontext.UserIDFromContext(ctx)
tenantID := appcontext.TenantIDFromContext(ctx)
query := `
SELECT id, user_id, bucket_name, prefix, expiration_days, enabled, created_at, updated_at
SELECT id, user_id, tenant_id, bucket_name, prefix, expiration_days, enabled, created_at, updated_at
FROM lifecycle_rules
WHERE bucket_name = $1 AND user_id = $2
WHERE bucket_name = $1 AND user_id = $2 AND tenant_id = $3
ORDER BY created_at DESC
`
rows, err := r.db.Query(ctx, query, bucketName, userId)
rows, err := r.db.Query(ctx, query, bucketName, userID, tenantID)
if err != nil {
return nil, errors.Wrap(errors.Internal, "failed to list lifecycle rules", err)
}
defer rows.Close()

var rules []*domain.LifecycleRule
rules := make([]*domain.LifecycleRule, 0)
for rows.Next() {
var rule domain.LifecycleRule
if err := rows.Scan(&rule.ID, &rule.UserID, &rule.BucketName, &rule.Prefix, &rule.ExpirationDays, &rule.Enabled, &rule.CreatedAt, &rule.UpdatedAt); err != nil {
if err := rows.Scan(&rule.ID, &rule.UserID, &rule.TenantID, &rule.BucketName, &rule.Prefix, &rule.ExpirationDays, &rule.Enabled, &rule.CreatedAt, &rule.UpdatedAt); err != nil {
return nil, errors.Wrap(errors.Internal, "failed to scan lifecycle rule", err)
}
rules = append(rules, &rule)
Expand All @@ -80,9 +83,10 @@ func (r *LifecycleRepository) List(ctx context.Context, bucketName string) ([]*d
}

func (r *LifecycleRepository) Delete(ctx context.Context, id uuid.UUID) error {
userId := appcontext.UserIDFromContext(ctx)
query := `DELETE FROM lifecycle_rules WHERE id = $1 AND user_id = $2`
cmd, err := r.db.Exec(ctx, query, id, userId)
userID := appcontext.UserIDFromContext(ctx)
tenantID := appcontext.TenantIDFromContext(ctx)
query := `DELETE FROM lifecycle_rules WHERE id = $1 AND user_id = $2 AND tenant_id = $3`
cmd, err := r.db.Exec(ctx, query, id, userID, tenantID)
if err != nil {
return errors.Wrap(errors.Internal, "failed to delete lifecycle rule", err)
}
Expand All @@ -96,7 +100,7 @@ func (r *LifecycleRepository) Delete(ctx context.Context, id uuid.UUID) error {
// This is intended for background workers and does not filter by user.
func (r *LifecycleRepository) GetEnabledRules(ctx context.Context) ([]*domain.LifecycleRule, error) {
query := `
SELECT id, user_id, bucket_name, prefix, expiration_days, enabled, created_at, updated_at
SELECT id, user_id, tenant_id, bucket_name, prefix, expiration_days, enabled, created_at, updated_at
FROM lifecycle_rules
WHERE enabled = TRUE
`
Expand All @@ -106,10 +110,10 @@ func (r *LifecycleRepository) GetEnabledRules(ctx context.Context) ([]*domain.Li
}
defer rows.Close()

var rules []*domain.LifecycleRule
rules := make([]*domain.LifecycleRule, 0)
for rows.Next() {
var rule domain.LifecycleRule
if err := rows.Scan(&rule.ID, &rule.UserID, &rule.BucketName, &rule.Prefix, &rule.ExpirationDays, &rule.Enabled, &rule.CreatedAt, &rule.UpdatedAt); err != nil {
if err := rows.Scan(&rule.ID, &rule.UserID, &rule.TenantID, &rule.BucketName, &rule.Prefix, &rule.ExpirationDays, &rule.Enabled, &rule.CreatedAt, &rule.UpdatedAt); err != nil {
return nil, errors.Wrap(errors.Internal, "failed to scan lifecycle rule", err)
}
rules = append(rules, &rule)
Expand Down
12 changes: 7 additions & 5 deletions internal/repositories/postgres/lifecycle_repo_unit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ func TestLifecycleRepository(t *testing.T) {
t.Parallel()
ctx := context.Background()
userID := uuid.New()
tenantID := uuid.New()
ctx = appcontext.WithUserID(ctx, userID)
ctx = appcontext.WithTenantID(ctx, tenantID)
bucketName := "test-bucket"

t.Run("Create", func(t *testing.T) {
Expand All @@ -42,7 +44,7 @@ func TestLifecycleRepository(t *testing.T) {
}

mock.ExpectExec("INSERT INTO lifecycle_rules").
WithArgs(rule.ID, rule.UserID, rule.BucketName, rule.Prefix, rule.ExpirationDays, rule.Enabled, rule.CreatedAt, rule.UpdatedAt).
WithArgs(rule.ID, rule.UserID, tenantID, rule.BucketName, rule.Prefix, rule.ExpirationDays, rule.Enabled, rule.CreatedAt, rule.UpdatedAt).
WillReturnResult(pgxmock.NewResult("INSERT", 1))

err := repo.Create(ctx, rule)
Expand All @@ -57,9 +59,9 @@ func TestLifecycleRepository(t *testing.T) {
id := uuid.New()

mock.ExpectQuery(selectLifecycleRules).
WithArgs(id, userID).
WillReturnRows(pgxmock.NewRows([]string{"id", "user_id", "bucket_name", "prefix", "expiration_days", "enabled", "created_at", "updated_at"}).
AddRow(id, userID, bucketName, testLifecyclePrefix, 30, true, time.Now(), time.Now()))
WithArgs(id, userID, tenantID).
WillReturnRows(pgxmock.NewRows([]string{"id", "user_id", "tenant_id", "bucket_name", "prefix", "expiration_days", "enabled", "created_at", "updated_at"}).
AddRow(id, userID, tenantID, bucketName, testLifecyclePrefix, 30, true, time.Now(), time.Now()))

rule, err := repo.Get(ctx, id)
require.NoError(t, err)
Expand All @@ -74,7 +76,7 @@ func TestLifecycleRepository(t *testing.T) {
id := uuid.New()

mock.ExpectExec("DELETE FROM lifecycle_rules").
WithArgs(id, userID).
WithArgs(id, userID, tenantID).
WillReturnResult(pgxmock.NewResult("DELETE", 1))

err := repo.Delete(ctx, id)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- +goose Down
ALTER TABLE lifecycle_rules DROP COLUMN IF EXISTS tenant_id;
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-- +goose Up
ALTER TABLE lifecycle_rules ADD COLUMN IF NOT EXISTS tenant_id UUID REFERENCES tenants(id) ON DELETE SET NULL;
CREATE INDEX IF NOT EXISTS idx_lifecycle_rules_tenant ON lifecycle_rules(tenant_id);

-- Backfill existing data using default_tenant_id from users table
UPDATE lifecycle_rules lr SET tenant_id = u.default_tenant_id FROM users u WHERE lr.user_id = u.id AND lr.tenant_id IS NULL;
Loading