package service import ( "errors" "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) ([]*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, clock Clock, random Random, settings *types.Settings) Account { return AccountImpl{ db: db, clock: clock, random: NewRandomImpl(), settings: settings, } } func (service AccountImpl) Add(user *types.User, name string) (*types.Account, error) { if user == nil { return nil, types.ErrInternal } newId, err := service.random.UUID() if err != nil { return nil, types.ErrInternal } err = service.validateAccount(name) if err != nil { return nil, err } account := &types.Account{ Id: newId, GroupId: user.Id, Name: name, CurrentBalance: 0, LastTransaction: nil, OinkBalance: 0, CreatedAt: service.clock.Now(), CreatedBy: user.Id, UpdatedAt: nil, UpdatedBy: nil, } err = service.db.Insert(account) if err != nil { return nil, err } savedAccount, err := service.db.Get(user.Id, newId) if err != nil { if errors.Is(err, db.ErrNotFound) { log.Error("Account not found after insert: %v", err) } return nil, types.ErrInternal } return savedAccount, nil } func (service AccountImpl) Update(user *types.User, id uuid.UUID, name string) (*types.Account, error) { if user == nil { return nil, types.ErrInternal } err := service.validateAccount(name) if err != nil { return nil, err } account, err := service.db.Get(user.Id, id) if err != nil { return nil, err } timestamp := service.clock.Now() account.Name = name account.UpdatedAt = ×tamp account.UpdatedBy = &user.Id err = service.db.Update(account) if err != nil { return nil, err } return account, nil } func (service AccountImpl) Get(user *types.User) ([]*types.Account, error) { if user == nil { return nil, types.ErrInternal } accounts, err := service.db.GetAll(user.GroupId) if err != nil { return nil, types.ErrInternal } return accounts, nil } func (service AccountImpl) Delete(user *types.User, id uuid.UUID) error { if user == nil { return types.ErrInternal } account, err := service.db.Get(user.GroupId, id) if err != nil { return err } if account.GroupId != user.GroupId { return types.ErrUnauthorized } err = service.db.Delete(account.Id) if err != nil { return err } return nil } func (service AccountImpl) validateAccount(name string) error { if name == "" { return errors.New("Please enter a value for the \"name\" field.") } else if !safeInputRegex.MatchString(name) { return errors.New("Please use only letters, dashes or numbers for \"name\".") } else { return nil } }