Skip to content

Commit

Permalink
feat: handling delete user from db not auth0
Browse files Browse the repository at this point in the history
  • Loading branch information
ericzorn93 committed Jan 4, 2025
1 parent 28c3bf1 commit d840248
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 25 deletions.
2 changes: 1 addition & 1 deletion apps/services/accounts-api/cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func run() error {
accountRepo := repositories.NewAccountRespository(params.Logger, params.DB)

// Create services
registrationService := services.NewRegistrationService(params.Logger, accountRepo)
registrationService := services.NewAccountService(params.Logger, accountRepo)

// Create Application
app := app.NewApp(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"fmt"
"libs/backend/boot"
"libs/backend/domain/user/entities"
"libs/backend/domain/user/valueobjects"
userValueObjects "libs/backend/domain/user/valueobjects"
accountsapiv1 "libs/backend/proto-gen/go/accounts/accountsapi/v1"
"libs/backend/proto-gen/go/accounts/accountsapi/v1/accountsapiv1connect"
accountsDomain "libs/backend/proto-gen/go/accounts/domain"
Expand All @@ -16,7 +16,7 @@ import (
)

// AuthHandler handles all gRPC endpoints for inbound webhooks
type RegistrationServiceHandler struct {
type AccountServiceHandler struct {
accountsapiv1connect.UnimplementedAccountServiceHandler

// Logger is the logger from the boot framework
Expand All @@ -27,20 +27,20 @@ type RegistrationServiceHandler struct {
}

// NewRegistrationHandler will return a pointer to the inbound webhooks API server
func NewRegistrationHandler(logger boot.Logger, app app.App) *RegistrationServiceHandler {
return &RegistrationServiceHandler{
func NewRegistrationHandler(logger boot.Logger, app app.App) *AccountServiceHandler {
return &AccountServiceHandler{
Logger: logger,
App: app,
}
}

// CreateAccount handles user creation and saves them in the database
func (r *RegistrationServiceHandler) CreateAccount(
func (r *AccountServiceHandler) CreateAccount(
ctx context.Context,
req *connect.Request[accountsapiv1.CreateAccountRequest],
) (*connect.Response[accountsapiv1.CreateAcountResponse], error) {
commonID := valueobjects.NewCommonIDFromString(req.Msg.CommonId)
emailAddress := valueobjects.NewEmailAddress(req.Msg.EmailAddress)
commonID := userValueObjects.NewCommonIDFromString(req.Msg.CommonId)
emailAddress := userValueObjects.NewEmailAddress(req.Msg.EmailAddress)

// Convert to user domain type
user := entities.NewUser(
Expand All @@ -56,21 +56,21 @@ func (r *RegistrationServiceHandler) CreateAccount(
}

// GetAccount handles user creation and saves them in the database
func (r *RegistrationServiceHandler) GetAccount(
func (r *AccountServiceHandler) GetAccount(
ctx context.Context,
req *connect.Request[accountsapiv1.GetAccountRequest],
) (*connect.Response[accountsapiv1.GetAccountResponse], error) {

// Parse the commonID vs emailAddress, depending on which one is passed
var commonID valueobjects.CommonID
var emailAddress valueobjects.EmailAddress
var commonID userValueObjects.CommonID
var emailAddress userValueObjects.EmailAddress

if req.Msg.CommonId != nil {
commonID = valueobjects.NewCommonIDFromString(*req.Msg.CommonId)
commonID = userValueObjects.NewCommonIDFromString(*req.Msg.CommonId)
}

if req.Msg.EmailAddress != nil {
emailAddress = valueobjects.NewEmailAddress(*req.Msg.EmailAddress)
emailAddress = userValueObjects.NewEmailAddress(*req.Msg.EmailAddress)
}

// Get the user from the database
Expand All @@ -91,3 +91,21 @@ func (r *RegistrationServiceHandler) GetAccount(

return connect.NewResponse(resp), nil
}

// DeleteAccount handles account deletion
func (r *AccountServiceHandler) DeleteAccount(
ctx context.Context,
req *connect.Request[accountsapiv1.DeleteAccountRequest],
) (*connect.Response[accountsapiv1.DeleteAccountResponse], error) {
// Parse incoming request data
parsedCommonID := userValueObjects.NewCommonIDFromString(req.Msg.CommonId)
hardDelete := req.Msg.HardDelete

// Perform deletion logic
deletedAt, err := r.App.RegistrationService.DeleteUser(ctx, parsedCommonID, hardDelete)
if err != nil {
return nil, connect.NewError(connect.CodeInternal, err)
}

return connect.NewResponse(&accountsapiv1.DeleteAccountResponse{DeletedAt: timestamppb.New(deletedAt)}), nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import (
"apps/services/accounts-api/internal/models"
"context"
"errors"
"fmt"
"libs/backend/boot"
userEntities "libs/backend/domain/user/entities"
userValueObjects "libs/backend/domain/user/valueobjects"
"log/slog"
"time"

"github.com/google/uuid"
"gorm.io/gorm"
Expand Down Expand Up @@ -69,7 +71,7 @@ func (r AccountRepository) GetAccountByEmailAddress(ctx context.Context, emailAd
r.Logger.Info("Getting account", slog.String("emailAdress", emailAdress.String()))

account := &models.Account{}
r.Database.First(account, "email_address = ?", emailAdress.Value())
r.Database.First(account, "email_address = ?", emailAdress)

if account.ID == uuid.Nil {
return userEntities.User{}, errors.New("account not found")
Expand All @@ -78,6 +80,34 @@ func (r AccountRepository) GetAccountByEmailAddress(ctx context.Context, emailAd
return r.convertAccountToUser(account), nil
}

// SoftDeleteAccountByCommonID will mark the user as deleted in the database with a timestamp
func (r AccountRepository) SoftDeleteAccountByCommonID(ctx context.Context, commonID userValueObjects.CommonID) (time.Time, error) {
r.Logger.Info("Handling soft deletion of account by commonID", slog.String("commonID", commonID.String()))

// Handle soft deletion in the database
deletedAccount := &models.Account{}
if err := r.Database.Delete(deletedAccount, "common_id = ?", commonID).Error; err != nil {
r.Logger.Error("Cannot soft delete the user by commonID", slog.String("commonID", commonID.String()))
return time.Time{}, fmt.Errorf("cannot soft delete account: %w", err)
}

return deletedAccount.DeletedAt.Time, nil
}

// HardDeleteAccountByCommonID will remove the user from the dataase
func (r AccountRepository) HardDeleteAccountByCommonID(ctx context.Context, commonID userValueObjects.CommonID) (time.Time, error) {
r.Logger.Info("Handling hard deletion of account by commonID", slog.Any("commonID", commonID.String()))

// Handle soft deletion in the database
deletedAccount := &models.Account{}
if err := r.Database.Unscoped().Delete(deletedAccount, "common_id = ?", commonID).Error; err != nil {
r.Logger.Error("Cannot hard delete the user by commonID", slog.String("commonID", commonID.String()))
return time.Time{}, fmt.Errorf("cannot hard delete accoutn: %w", err)
}

return deletedAccount.DeletedAt.Time, nil
}

// convertAccountToUser converts an account to a user
func (r AccountRepository) convertAccountToUser(account *models.Account) userEntities.User {
parsedCommonID := userValueObjects.NewCommonIDFromUUID(account.CommonID)
Expand Down
7 changes: 7 additions & 0 deletions apps/services/accounts-api/internal/app/ports/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
userEntities "libs/backend/domain/user/entities"
userValueObjects "libs/backend/domain/user/valueobjects"
"time"
)

// AccountRepository is the interface for the account repository
Expand All @@ -16,4 +17,10 @@ type AccountRepository interface {

// GetAccountByEmailAddress gets an account from the database by email address
GetAccountByEmailAddress(context.Context, userValueObjects.EmailAddress) (userEntities.User, error)

// SoftDeleteAccountByCommonID will soft delete the user
SoftDeleteAccountByCommonID(context.Context, userValueObjects.CommonID) (time.Time, error)

// HardDeleteAccountByCommonID will hard delete the user
HardDeleteAccountByCommonID(context.Context, userValueObjects.CommonID) (time.Time, error)
}
4 changes: 4 additions & 0 deletions apps/services/accounts-api/internal/app/ports/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
userEntities "libs/backend/domain/user/entities"
userValueObjects "libs/backend/domain/user/valueobjects"
"time"
)

// AccountService is the interface for the registration service
Expand All @@ -13,4 +14,7 @@ type AccountService interface {

// GetUser gets a user from the system
GetUser(ctx context.Context, commonID userValueObjects.CommonID, emailAddress userValueObjects.EmailAddress) (userEntities.User, error)

// Delete user will delete the user from the system (hard or soft deletion)
DeleteUser(ctx context.Context, commonID userValueObjects.CommonID, hardDelete bool) (time.Time, error)
}
4 changes: 0 additions & 4 deletions apps/services/accounts-api/internal/domain/generic.go

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,28 @@ import (
userEntities "libs/backend/domain/user/entities"
userValueObjects "libs/backend/domain/user/valueobjects"
"log/slog"
"time"
)

// RegistrationService is the registration service
type RegistrationService struct {
// AccountService is the registration service
type AccountService struct {
// Logger is the logger from the boot framework
Logger boot.Logger

// AccountRepository is the account repository
AccountRepository ports.AccountRepository
}

// NewRegistrationService creates a new registration service
func NewRegistrationService(logger boot.Logger, accountRepository ports.AccountRepository) RegistrationService {
return RegistrationService{
// NewAccountService creates a new registration service
func NewAccountService(logger boot.Logger, accountRepository ports.AccountRepository) AccountService {
return AccountService{
Logger: logger,
AccountRepository: accountRepository,
}
}

// RegisterUser registers a user in the system and the database
func (s RegistrationService) RegisterUser(ctx context.Context, user userEntities.User) error {
func (s AccountService) RegisterUser(ctx context.Context, user userEntities.User) error {
s.Logger.Info("Registering user", slog.String("commonID", user.CommonID.String()))

// Create account
Expand All @@ -41,7 +42,7 @@ func (s RegistrationService) RegisterUser(ctx context.Context, user userEntities
}

// GetUser gets a user from the system
func (s RegistrationService) GetUser(ctx context.Context, commonID userValueObjects.CommonID, emailAddress userValueObjects.EmailAddress) (userEntities.User, error) {
func (s AccountService) GetUser(ctx context.Context, commonID userValueObjects.CommonID, emailAddress userValueObjects.EmailAddress) (userEntities.User, error) {
var err error

s.Logger.Info("Getting user", slog.String("commonID", commonID.String()))
Expand All @@ -66,3 +67,24 @@ func (s RegistrationService) GetUser(ctx context.Context, commonID userValueObje

return user, nil
}

// Delete user will delete the user from the system (hard or soft deletion)
func (s AccountService) DeleteUser(ctx context.Context, commonID userValueObjects.CommonID, hardDelete bool) (time.Time, error) {
s.Logger.Info("Deleting user by commonID", slog.String("commonID", commonID.String()), slog.Bool("hardDelete", hardDelete))

switch {
case !hardDelete:
deletedAt, err := s.AccountRepository.SoftDeleteAccountByCommonID(ctx, commonID)
if err != nil {
return time.Time{}, err
}
return deletedAt, nil
default:
deletedAt, err := s.AccountRepository.HardDeleteAccountByCommonID(ctx, commonID)
if err != nil {
return time.Time{}, err
}

return deletedAt, nil
}
}

0 comments on commit d840248

Please sign in to comment.