Some checks failed
Build Docker Image / Build-Docker-Image (push) Has been cancelled
240 lines
5.5 KiB
Go
240 lines
5.5 KiB
Go
package service
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"spend-sparrow/db"
|
|
"spend-sparrow/log"
|
|
"spend-sparrow/types"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
|
)
|
|
|
|
var (
|
|
treasureChestMetric = promauto.NewCounterVec(
|
|
prometheus.CounterOpts{
|
|
Name: "spendsparrow_treasurechest_total",
|
|
Help: "The total of treasurechest operations",
|
|
},
|
|
[]string{"operation"},
|
|
)
|
|
)
|
|
|
|
type TreasureChest interface {
|
|
Add(user *types.User, parentId, name string) (*types.TreasureChest, error)
|
|
Update(user *types.User, id, parentId, name string) (*types.TreasureChest, error)
|
|
Get(user *types.User, id string) (*types.TreasureChest, error)
|
|
GetAll(user *types.User) ([]*types.TreasureChest, error)
|
|
Delete(user *types.User, id string) error
|
|
}
|
|
|
|
type TreasureChestImpl struct {
|
|
db db.TreasureChest
|
|
clock Clock
|
|
random Random
|
|
settings *types.Settings
|
|
}
|
|
|
|
func NewTreasureChest(db db.TreasureChest, random Random, clock Clock, settings *types.Settings) TreasureChest {
|
|
return TreasureChestImpl{
|
|
db: db,
|
|
clock: clock,
|
|
random: NewRandom(),
|
|
settings: settings,
|
|
}
|
|
}
|
|
|
|
func (s TreasureChestImpl) Add(user *types.User, parentId, name string) (*types.TreasureChest, error) {
|
|
treasureChestMetric.WithLabelValues("add").Inc()
|
|
|
|
if user == nil {
|
|
return nil, ErrUnauthorized
|
|
}
|
|
|
|
newId, err := s.random.UUID()
|
|
if err != nil {
|
|
return nil, types.ErrInternal
|
|
}
|
|
|
|
err = validateString(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
parentUuid := uuid.Nil
|
|
if parentId != "" {
|
|
parent, err := s.Get(user, parentId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if parent.ParentId != uuid.Nil {
|
|
return nil, fmt.Errorf("only a depth of 1 allowed: %w", ErrBadRequest)
|
|
}
|
|
parentUuid = parent.Id
|
|
}
|
|
|
|
treasureChest := &types.TreasureChest{
|
|
Id: newId,
|
|
ParentId: parentUuid,
|
|
UserId: user.Id,
|
|
|
|
Name: name,
|
|
|
|
CurrentBalance: 0,
|
|
|
|
CreatedAt: s.clock.Now(),
|
|
CreatedBy: user.Id,
|
|
UpdatedAt: nil,
|
|
UpdatedBy: nil,
|
|
}
|
|
|
|
err = s.db.Insert(user.Id, treasureChest)
|
|
if err != nil {
|
|
return nil, types.ErrInternal
|
|
}
|
|
|
|
savedtreasureChest, err := s.db.Get(user.Id, newId)
|
|
if err != nil {
|
|
log.Error("treasureChest %v not found after insert: %v", newId, err)
|
|
return nil, types.ErrInternal
|
|
}
|
|
return savedtreasureChest, nil
|
|
}
|
|
|
|
func (s TreasureChestImpl) Update(user *types.User, idStr, parentId, name string) (*types.TreasureChest, error) {
|
|
treasureChestMetric.WithLabelValues("update").Inc()
|
|
if user == nil {
|
|
return nil, ErrUnauthorized
|
|
}
|
|
err := validateString(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
id, err := uuid.Parse(idStr)
|
|
if err != nil {
|
|
log.Error("treasureChest update: %v", err)
|
|
return nil, fmt.Errorf("could not parse Id: %w", ErrBadRequest)
|
|
}
|
|
|
|
treasureChest, err := s.db.Get(user.Id, id)
|
|
if err != nil {
|
|
if err == db.ErrNotFound {
|
|
return nil, fmt.Errorf("treasureChest %v not found: %w", idStr, ErrBadRequest)
|
|
}
|
|
return nil, types.ErrInternal
|
|
}
|
|
|
|
parentUuid := uuid.Nil
|
|
if parentId != "" {
|
|
parent, err := s.Get(user, parentId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if parent.ParentId != uuid.Nil {
|
|
return nil, fmt.Errorf("only a depth of 1 allowed: %w", ErrBadRequest)
|
|
}
|
|
|
|
children, err := s.db.GetAllByParentId(user.Id, treasureChest.Id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(children) > 0 {
|
|
return nil, fmt.Errorf("only a depth of 1 allowed: %w", ErrBadRequest)
|
|
}
|
|
|
|
parentUuid = parent.Id
|
|
}
|
|
|
|
timestamp := s.clock.Now()
|
|
treasureChest.Name = name
|
|
treasureChest.ParentId = parentUuid
|
|
treasureChest.UpdatedAt = ×tamp
|
|
treasureChest.UpdatedBy = &user.Id
|
|
|
|
err = s.db.Update(user.Id, treasureChest)
|
|
if err != nil {
|
|
return nil, types.ErrInternal
|
|
}
|
|
|
|
return treasureChest, nil
|
|
}
|
|
|
|
func (s TreasureChestImpl) Get(user *types.User, id string) (*types.TreasureChest, error) {
|
|
treasureChestMetric.WithLabelValues("get").Inc()
|
|
|
|
if user == nil {
|
|
return nil, ErrUnauthorized
|
|
}
|
|
uuid, err := uuid.Parse(id)
|
|
if err != nil {
|
|
log.Error("treasureChest get: %v", err)
|
|
return nil, fmt.Errorf("could not parse Id: %w", ErrBadRequest)
|
|
}
|
|
|
|
treasureChest, err := s.db.Get(user.Id, uuid)
|
|
if err != nil {
|
|
if err == db.ErrNotFound {
|
|
return nil, fmt.Errorf("treasureChest %v not found: %w", id, ErrBadRequest)
|
|
}
|
|
return nil, types.ErrInternal
|
|
}
|
|
|
|
return treasureChest, nil
|
|
}
|
|
|
|
func (s TreasureChestImpl) GetAll(user *types.User) ([]*types.TreasureChest, error) {
|
|
treasureChestMetric.WithLabelValues("get_all").Inc()
|
|
if user == nil {
|
|
return nil, ErrUnauthorized
|
|
}
|
|
|
|
treasureChests, err := s.db.GetAll(user.Id)
|
|
if err != nil {
|
|
return nil, types.ErrInternal
|
|
}
|
|
|
|
return treasureChests, nil
|
|
}
|
|
|
|
func (s TreasureChestImpl) Delete(user *types.User, idStr string) error {
|
|
treasureChestMetric.WithLabelValues("delete").Inc()
|
|
if user == nil {
|
|
return ErrUnauthorized
|
|
}
|
|
id, err := uuid.Parse(idStr)
|
|
if err != nil {
|
|
log.Error("treasureChest delete: %v", err)
|
|
return fmt.Errorf("could not parse Id: %w", ErrBadRequest)
|
|
}
|
|
|
|
treasureChest, err := s.db.Get(user.Id, id)
|
|
if err != nil {
|
|
if err == db.ErrNotFound {
|
|
return fmt.Errorf("treasureChest %v not found: %w", idStr, ErrBadRequest)
|
|
}
|
|
return types.ErrInternal
|
|
}
|
|
|
|
if treasureChest.UserId != user.Id {
|
|
return types.ErrUnauthorized
|
|
}
|
|
|
|
children, err := s.db.GetAllByParentId(user.Id, treasureChest.Id)
|
|
if err != nil {
|
|
log.Error("treasureChest delete: %v", err)
|
|
return types.ErrInternal
|
|
}
|
|
if len(children) > 0 {
|
|
return fmt.Errorf("TreasureChest %v has children: %w", idStr, ErrBadRequest)
|
|
}
|
|
|
|
err = s.db.Delete(user.Id, treasureChest.Id)
|
|
if err != nil {
|
|
return types.ErrInternal
|
|
}
|
|
|
|
return nil
|
|
}
|