Files
spend-sparrow/service/account.go
Tim Wundenberg fecf53fd20
Some checks failed
Build Docker Image / Build-Docker-Image (push) Failing after 3m55s
feat(account): #49 refactor error handling
2025-05-08 22:58:14 +02:00

181 lines
3.6 KiB
Go

package service
import (
"fmt"
"regexp"
"spend-sparrow/db"
"spend-sparrow/log"
"spend-sparrow/types"
"github.com/google/uuid"
)
var (
safeInputRegex = regexp.MustCompile(`^[a-zA-Z0-9äöüß -]+$`)
)
type Account interface {
Add(user *types.User, name string) (*types.Account, error)
Update(user *types.User, id uuid.UUID, name string) (*types.Account, error)
Get(user *types.User, id uuid.UUID) (*types.Account, error)
GetAll(user *types.User) ([]*types.Account, error)
Delete(user *types.User, id uuid.UUID) error
}
type AccountImpl struct {
db db.Account
clock Clock
random Random
settings *types.Settings
}
func NewAccountImpl(db db.Account, random Random, clock Clock, settings *types.Settings) Account {
return AccountImpl{
db: db,
clock: clock,
random: NewRandomImpl(),
settings: settings,
}
}
func (s AccountImpl) Add(user *types.User, name string) (*types.Account, error) {
if user == nil {
return nil, ErrUnauthorized
}
newId, err := s.random.UUID()
if err != nil {
return nil, types.ErrInternal
}
err = s.validateAccount(name)
if err != nil {
return nil, err
}
account := &types.Account{
Id: newId,
UserId: user.Id,
Name: name,
CurrentBalance: 0,
LastTransaction: nil,
OinkBalance: 0,
CreatedAt: s.clock.Now(),
CreatedBy: user.Id,
UpdatedAt: nil,
UpdatedBy: nil,
}
err = s.db.Insert(user.Id, account)
if err != nil {
return nil, types.ErrInternal
}
savedAccount, err := s.db.Get(user.Id, newId)
if err != nil {
log.Error("account %v not found after insert: %v", newId, err)
return nil, types.ErrInternal
}
return savedAccount, nil
}
func (s AccountImpl) Update(user *types.User, id uuid.UUID, name string) (*types.Account, error) {
if user == nil {
return nil, ErrUnauthorized
}
err := s.validateAccount(name)
if err != nil {
return nil, err
}
account, err := s.db.Get(user.Id, id)
if err != nil {
if err == db.ErrNotFound {
return nil, fmt.Errorf("account %v not found: %w", id, ErrBadRequest)
}
return nil, types.ErrInternal
}
timestamp := s.clock.Now()
account.Name = name
account.UpdatedAt = &timestamp
account.UpdatedBy = &user.Id
err = s.db.Update(user.Id, account)
if err != nil {
return nil, types.ErrInternal
}
return account, nil
}
func (s AccountImpl) Get(user *types.User, id uuid.UUID) (*types.Account, error) {
if user == nil {
return nil, ErrUnauthorized
}
account, err := s.db.Get(user.Id, id)
if err != nil {
if err == db.ErrNotFound {
return nil, fmt.Errorf("account %v not found: %w", id, ErrBadRequest)
}
return nil, types.ErrInternal
}
return account, nil
}
func (s AccountImpl) GetAll(user *types.User) ([]*types.Account, error) {
if user == nil {
return nil, ErrUnauthorized
}
accounts, err := s.db.GetAll(user.Id)
if err != nil {
return nil, types.ErrInternal
}
return accounts, nil
}
func (s AccountImpl) Delete(user *types.User, id uuid.UUID) error {
if user == nil {
return ErrUnauthorized
}
account, err := s.db.Get(user.Id, id)
if err != nil {
if err == db.ErrNotFound {
return fmt.Errorf("account %v not found: %w", id, ErrBadRequest)
}
return types.ErrInternal
}
if account.UserId != user.Id {
return types.ErrUnauthorized
}
err = s.db.Delete(user.Id, account.Id)
if err != nil {
return types.ErrInternal
}
return nil
}
func (s AccountImpl) validateAccount(name string) error {
if name == "" {
return fmt.Errorf("field \"name\" needs to be set: %w", ErrBadRequest)
} else if !safeInputRegex.MatchString(name) {
return fmt.Errorf("use only letters, dashes and spaces for \"name\": %w", ErrBadRequest)
} else {
return nil
}
}