feat(treasurechest): #66 remove db interface for treasure chests
This commit was merged in pull request #75.
This commit is contained in:
@@ -1,151 +0,0 @@
|
|||||||
package db
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"spend-sparrow/log"
|
|
||||||
"spend-sparrow/types"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
)
|
|
||||||
|
|
||||||
// While it may be duplicated to check for userId in the database access, it serves as a security layer
|
|
||||||
type TreasureChest interface {
|
|
||||||
Insert(userId uuid.UUID, treasureChest *types.TreasureChest) error
|
|
||||||
Update(userId uuid.UUID, treasureChest *types.TreasureChest) error
|
|
||||||
GetAll(userId uuid.UUID) ([]*types.TreasureChest, error)
|
|
||||||
GetAllByParentId(userId uuid.UUID, parentId uuid.UUID) ([]*types.TreasureChest, error)
|
|
||||||
Get(userId uuid.UUID, id uuid.UUID) (*types.TreasureChest, error)
|
|
||||||
Delete(userId uuid.UUID, id uuid.UUID) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type TreasureChestSqlite struct {
|
|
||||||
db *sqlx.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewTreasureChestSqlite(db *sqlx.DB) *TreasureChestSqlite {
|
|
||||||
return &TreasureChestSqlite{db: db}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db TreasureChestSqlite) Insert(userId uuid.UUID, treasureChest *types.TreasureChest) error {
|
|
||||||
|
|
||||||
_, err := db.db.Exec(`
|
|
||||||
INSERT INTO treasure_chest (id, parent_id, user_id, name, current_balance, created_at, created_by)
|
|
||||||
VALUES (?,?,?,?,?,?,?)`, treasureChest.Id, treasureChest.ParentId, userId, treasureChest.Name, 0, treasureChest.CreatedAt, treasureChest.CreatedBy)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("treasureChest Insert: %v", err)
|
|
||||||
return types.ErrInternal
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db TreasureChestSqlite) Update(userId uuid.UUID, treasureChest *types.TreasureChest) error {
|
|
||||||
|
|
||||||
r, err := db.db.Exec(`
|
|
||||||
UPDATE treasure_chest
|
|
||||||
SET
|
|
||||||
parent_id = ?,
|
|
||||||
name = ?,
|
|
||||||
current_balance = ?,
|
|
||||||
updated_at = ?,
|
|
||||||
updated_by = ?
|
|
||||||
WHERE id = ?
|
|
||||||
AND user_id = ?`, treasureChest.ParentId, treasureChest.Name, treasureChest.CurrentBalance, treasureChest.UpdatedAt, treasureChest.UpdatedBy, treasureChest.Id, userId)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("treasureChest Update: %v", err)
|
|
||||||
return types.ErrInternal
|
|
||||||
}
|
|
||||||
rows, err := r.RowsAffected()
|
|
||||||
if err != nil {
|
|
||||||
log.Error("treasureChest Update: %v", err)
|
|
||||||
return types.ErrInternal
|
|
||||||
}
|
|
||||||
|
|
||||||
if rows == 0 {
|
|
||||||
log.Info("treasureChest Update: not found")
|
|
||||||
return ErrNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db TreasureChestSqlite) GetAll(userId uuid.UUID) ([]*types.TreasureChest, error) {
|
|
||||||
|
|
||||||
treasureChests := make([]*types.TreasureChest, 0)
|
|
||||||
err := db.db.Select(&treasureChests, `
|
|
||||||
SELECT
|
|
||||||
id, parent_id, user_id, name, current_balance,
|
|
||||||
created_at, created_by, updated_at, updated_by
|
|
||||||
FROM treasure_chest
|
|
||||||
WHERE user_id = ?
|
|
||||||
ORDER BY name`, userId)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("treasureChest GetAll: %v", err)
|
|
||||||
return nil, types.ErrInternal
|
|
||||||
}
|
|
||||||
|
|
||||||
return treasureChests, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db TreasureChestSqlite) GetAllByParentId(userId uuid.UUID, parentId uuid.UUID) ([]*types.TreasureChest, error) {
|
|
||||||
|
|
||||||
treasureChests := make([]*types.TreasureChest, 0)
|
|
||||||
err := db.db.Select(&treasureChests, `
|
|
||||||
SELECT
|
|
||||||
id, parent_id, user_id, name, current_balance,
|
|
||||||
created_at, created_by, updated_at, updated_by
|
|
||||||
FROM treasure_chest
|
|
||||||
WHERE user_id = ?
|
|
||||||
AND parent_id = ?
|
|
||||||
ORDER BY name`, userId, parentId)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("treasureChest GetAll: %v", err)
|
|
||||||
return nil, types.ErrInternal
|
|
||||||
}
|
|
||||||
|
|
||||||
return treasureChests, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db TreasureChestSqlite) Get(userId uuid.UUID, id uuid.UUID) (*types.TreasureChest, error) {
|
|
||||||
|
|
||||||
treasureChest := &types.TreasureChest{}
|
|
||||||
err := db.db.Get(treasureChest, `
|
|
||||||
SELECT
|
|
||||||
id, parent_id, user_id, name, current_balance,
|
|
||||||
created_at, created_by, updated_at, updated_by
|
|
||||||
FROM treasure_chest
|
|
||||||
WHERE user_id = ?
|
|
||||||
AND id = ?`, userId, id)
|
|
||||||
if err != nil {
|
|
||||||
if err == sql.ErrNoRows {
|
|
||||||
return nil, ErrNotFound
|
|
||||||
}
|
|
||||||
log.Error("treasureChest Get: %v", err)
|
|
||||||
return nil, types.ErrInternal
|
|
||||||
}
|
|
||||||
|
|
||||||
return treasureChest, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db TreasureChestSqlite) Delete(userId uuid.UUID, id uuid.UUID) error {
|
|
||||||
|
|
||||||
res, err := db.db.Exec("DELETE FROM treasure_chest WHERE id = ? and user_id = ?", id, userId)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("treasureChest Delete: %v", err)
|
|
||||||
return types.ErrInternal
|
|
||||||
}
|
|
||||||
|
|
||||||
rows, err := res.RowsAffected()
|
|
||||||
if err != nil {
|
|
||||||
log.Error("treasureChest Delete: %v", err)
|
|
||||||
return types.ErrInternal
|
|
||||||
}
|
|
||||||
|
|
||||||
if rows == 0 {
|
|
||||||
log.Info("treasureChest Delete: not found")
|
|
||||||
return ErrNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
3
main.go
3
main.go
@@ -107,7 +107,6 @@ func createHandler(d *sqlx.DB, serverSettings *types.Settings) http.Handler {
|
|||||||
var router = http.NewServeMux()
|
var router = http.NewServeMux()
|
||||||
|
|
||||||
authDb := db.NewAuthSqlite(d)
|
authDb := db.NewAuthSqlite(d)
|
||||||
treasureChestDb := db.NewTreasureChestSqlite(d)
|
|
||||||
|
|
||||||
randomService := service.NewRandom()
|
randomService := service.NewRandom()
|
||||||
clockService := service.NewClock()
|
clockService := service.NewClock()
|
||||||
@@ -115,7 +114,7 @@ func createHandler(d *sqlx.DB, serverSettings *types.Settings) http.Handler {
|
|||||||
|
|
||||||
authService := service.NewAuth(authDb, randomService, clockService, mailService, serverSettings)
|
authService := service.NewAuth(authDb, randomService, clockService, mailService, serverSettings)
|
||||||
accountService := service.NewAccount(d, randomService, clockService, serverSettings)
|
accountService := service.NewAccount(d, randomService, clockService, serverSettings)
|
||||||
treasureChestService := service.NewTreasureChest(treasureChestDb, randomService, clockService, serverSettings)
|
treasureChestService := service.NewTreasureChest(d, randomService, clockService, serverSettings)
|
||||||
transactionService := service.NewTransaction(d, randomService, clockService, serverSettings)
|
transactionService := service.NewTransaction(d, randomService, clockService, serverSettings)
|
||||||
|
|
||||||
render := handler.NewRender()
|
render := handler.NewRender()
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"spend-sparrow/types"
|
"spend-sparrow/types"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
)
|
)
|
||||||
@@ -31,13 +32,13 @@ type TreasureChest interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type TreasureChestImpl struct {
|
type TreasureChestImpl struct {
|
||||||
db db.TreasureChest
|
db *sqlx.DB
|
||||||
clock Clock
|
clock Clock
|
||||||
random Random
|
random Random
|
||||||
settings *types.Settings
|
settings *types.Settings
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTreasureChest(db db.TreasureChest, random Random, clock Clock, settings *types.Settings) TreasureChest {
|
func NewTreasureChest(db *sqlx.DB, random Random, clock Clock, settings *types.Settings) TreasureChest {
|
||||||
return TreasureChestImpl{
|
return TreasureChestImpl{
|
||||||
db: db,
|
db: db,
|
||||||
clock: clock,
|
clock: clock,
|
||||||
@@ -90,17 +91,15 @@ func (s TreasureChestImpl) Add(user *types.User, parentId, name string) (*types.
|
|||||||
UpdatedBy: nil,
|
UpdatedBy: nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.db.Insert(user.Id, treasureChest)
|
r, err := s.db.NamedExec(`
|
||||||
|
INSERT INTO treasure_chest (id, parent_id, user_id, name, current_balance, created_at, created_by)
|
||||||
|
VALUES (:id, :parent_id, :user_id, :name, :current_balance, :created_at, :created_by)`, treasureChest)
|
||||||
|
err = db.TransformAndLogDbError("treasureChest Insert", r, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, types.ErrInternal
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
savedtreasureChest, err := s.db.Get(user.Id, newId)
|
return treasureChest, nil
|
||||||
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) {
|
func (s TreasureChestImpl) Update(user *types.User, idStr, parentId, name string) (*types.TreasureChest, error) {
|
||||||
@@ -118,10 +117,12 @@ func (s TreasureChestImpl) Update(user *types.User, idStr, parentId, name string
|
|||||||
return nil, fmt.Errorf("could not parse Id: %w", ErrBadRequest)
|
return nil, fmt.Errorf("could not parse Id: %w", ErrBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
treasureChest, err := s.db.Get(user.Id, id)
|
treasureChest := &types.TreasureChest{}
|
||||||
|
err = s.db.Get(treasureChest, `SELECT * FROM treasure_chest WHERE user_id = ? AND id = ?`, user.Id, id)
|
||||||
|
err = db.TransformAndLogDbError("treasureChest Update", nil, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == db.ErrNotFound {
|
if err == db.ErrNotFound {
|
||||||
return nil, fmt.Errorf("treasureChest %v not found: %w", idStr, ErrBadRequest)
|
return nil, fmt.Errorf("treasureChest %v not found: %w", idStr, err)
|
||||||
}
|
}
|
||||||
return nil, types.ErrInternal
|
return nil, types.ErrInternal
|
||||||
}
|
}
|
||||||
@@ -132,16 +133,14 @@ func (s TreasureChestImpl) Update(user *types.User, idStr, parentId, name string
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if parent.ParentId != uuid.Nil {
|
var childCount int
|
||||||
return nil, fmt.Errorf("only a depth of 1 allowed: %w", ErrBadRequest)
|
err = s.db.Get(&childCount, `SELECT COUNT(*) FROM treasure_chest WHERE user_id = ? AND parent_id = ?`, user.Id, id)
|
||||||
}
|
err = db.TransformAndLogDbError("treasureChest Update", nil, err)
|
||||||
|
|
||||||
children, err := s.db.GetAllByParentId(user.Id, treasureChest.Id)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(children) > 0 {
|
if parent.ParentId != uuid.Nil || childCount > 0 {
|
||||||
return nil, fmt.Errorf("only a depth of 1 allowed: %w", ErrBadRequest)
|
return nil, fmt.Errorf("only one level allowed: %w", ErrBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
parentUuid = parent.Id
|
parentUuid = parent.Id
|
||||||
@@ -153,9 +152,19 @@ func (s TreasureChestImpl) Update(user *types.User, idStr, parentId, name string
|
|||||||
treasureChest.UpdatedAt = ×tamp
|
treasureChest.UpdatedAt = ×tamp
|
||||||
treasureChest.UpdatedBy = &user.Id
|
treasureChest.UpdatedBy = &user.Id
|
||||||
|
|
||||||
err = s.db.Update(user.Id, treasureChest)
|
r, err := s.db.NamedExec(`
|
||||||
|
UPDATE treasure_chest
|
||||||
|
SET
|
||||||
|
parent_id = :parent_id,
|
||||||
|
name = :name,
|
||||||
|
current_balance = :current_balance,
|
||||||
|
updated_at = :updated_at,
|
||||||
|
updated_by = :updated_by
|
||||||
|
WHERE id = :id
|
||||||
|
AND user_id = :user_id`, treasureChest)
|
||||||
|
err = db.TransformAndLogDbError("treasureChest Update", r, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, types.ErrInternal
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return treasureChest, nil
|
return treasureChest, nil
|
||||||
@@ -173,10 +182,12 @@ func (s TreasureChestImpl) Get(user *types.User, id string) (*types.TreasureChes
|
|||||||
return nil, fmt.Errorf("could not parse Id: %w", ErrBadRequest)
|
return nil, fmt.Errorf("could not parse Id: %w", ErrBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
treasureChest, err := s.db.Get(user.Id, uuid)
|
treasureChest := &types.TreasureChest{}
|
||||||
|
err = s.db.Get(treasureChest, `SELECT * FROM treasure_chest WHERE user_id = ? AND id = ?`, user.Id, uuid)
|
||||||
|
err = db.TransformAndLogDbError("treasureChest Get", nil, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == db.ErrNotFound {
|
if err == db.ErrNotFound {
|
||||||
return nil, fmt.Errorf("treasureChest %v not found: %w", id, ErrBadRequest)
|
return nil, fmt.Errorf("treasureChest %v not found: %w", id, err)
|
||||||
}
|
}
|
||||||
return nil, types.ErrInternal
|
return nil, types.ErrInternal
|
||||||
}
|
}
|
||||||
@@ -190,9 +201,11 @@ func (s TreasureChestImpl) GetAll(user *types.User) ([]*types.TreasureChest, err
|
|||||||
return nil, ErrUnauthorized
|
return nil, ErrUnauthorized
|
||||||
}
|
}
|
||||||
|
|
||||||
treasureChests, err := s.db.GetAll(user.Id)
|
treasureChests := make([]*types.TreasureChest, 0)
|
||||||
|
err := s.db.Select(&treasureChests, `SELECT * FROM treasure_chest WHERE user_id = ?`, user.Id)
|
||||||
|
err = db.TransformAndLogDbError("treasureChest GetAll", nil, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, types.ErrInternal
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return sortTree(treasureChests), nil
|
return sortTree(treasureChests), nil
|
||||||
@@ -209,30 +222,21 @@ func (s TreasureChestImpl) Delete(user *types.User, idStr string) error {
|
|||||||
return fmt.Errorf("could not parse Id: %w", ErrBadRequest)
|
return fmt.Errorf("could not parse Id: %w", ErrBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
treasureChest, err := s.db.Get(user.Id, id)
|
childCount := 0
|
||||||
|
err = s.db.Get(&childCount, `SELECT COUNT(*) FROM treasure_chest WHERE user_id = ? AND parent_id = ?`, user.Id, id)
|
||||||
|
err = db.TransformAndLogDbError("treasureChest Delete", nil, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == db.ErrNotFound {
|
return err
|
||||||
return fmt.Errorf("treasureChest %v not found: %w", idStr, ErrBadRequest)
|
|
||||||
}
|
|
||||||
return types.ErrInternal
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if treasureChest.UserId != user.Id {
|
if childCount > 0 {
|
||||||
return types.ErrUnauthorized
|
return fmt.Errorf("treasure chest has children: %w", ErrBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
children, err := s.db.GetAllByParentId(user.Id, treasureChest.Id)
|
r, err := s.db.Exec(`DELETE FROM treasure_chest WHERE id = ? AND user_id = ?`, id, user.Id)
|
||||||
|
err = db.TransformAndLogDbError("treasureChest Delete", r, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("treasureChest delete: %v", err)
|
return 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
|
return nil
|
||||||
|
|||||||
Reference in New Issue
Block a user