mirror of
https://github.com/aclindsa/moneygo.git
synced 2025-07-02 20:28:38 -04:00
Use SQL transactions for the entirety of every request
This commit is contained in:
@ -129,10 +129,10 @@ func (al *AccountList) Read(json_str string) error {
|
||||
return dec.Decode(al)
|
||||
}
|
||||
|
||||
func GetAccount(db *DB, accountid int64, userid int64) (*Account, error) {
|
||||
func GetAccount(tx *Tx, accountid int64, userid int64) (*Account, error) {
|
||||
var a Account
|
||||
|
||||
err := db.SelectOne(&a, "SELECT * from accounts where UserId=? AND AccountId=?", userid, accountid)
|
||||
err := tx.SelectOne(&a, "SELECT * from accounts where UserId=? AND AccountId=?", userid, accountid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -150,10 +150,10 @@ func GetAccountTx(transaction *gorp.Transaction, accountid int64, userid int64)
|
||||
return &a, nil
|
||||
}
|
||||
|
||||
func GetAccounts(db *DB, userid int64) (*[]Account, error) {
|
||||
func GetAccounts(tx *Tx, userid int64) (*[]Account, error) {
|
||||
var accounts []Account
|
||||
|
||||
_, err := db.Select(&accounts, "SELECT * from accounts where UserId=?", userid)
|
||||
_, err := tx.Select(&accounts, "SELECT * from accounts where UserId=?", userid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -293,12 +293,7 @@ func (cae CircularAccountsError) Error() string {
|
||||
return "Would result in circular account relationship"
|
||||
}
|
||||
|
||||
func insertUpdateAccount(db *DB, a *Account, insert bool) error {
|
||||
transaction, err := db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
func insertUpdateAccount(tx *Tx, a *Account, insert bool) error {
|
||||
found := make(map[int64]bool)
|
||||
if !insert {
|
||||
found[a.AccountId] = true
|
||||
@ -308,14 +303,12 @@ func insertUpdateAccount(db *DB, a *Account, insert bool) error {
|
||||
for parentid != -1 {
|
||||
depth += 1
|
||||
if depth > 100 {
|
||||
transaction.Rollback()
|
||||
return TooMuchNestingError{}
|
||||
}
|
||||
|
||||
var a Account
|
||||
err := transaction.SelectOne(&a, "SELECT * from accounts where AccountId=?", parentid)
|
||||
err := tx.SelectOne(&a, "SELECT * from accounts where AccountId=?", parentid)
|
||||
if err != nil {
|
||||
transaction.Rollback()
|
||||
return ParentAccountMissingError{}
|
||||
}
|
||||
|
||||
@ -327,107 +320,79 @@ func insertUpdateAccount(db *DB, a *Account, insert bool) error {
|
||||
found[parentid] = true
|
||||
parentid = a.ParentAccountId
|
||||
if _, ok := found[parentid]; ok {
|
||||
transaction.Rollback()
|
||||
return CircularAccountsError{}
|
||||
}
|
||||
}
|
||||
|
||||
if insert {
|
||||
err = transaction.Insert(a)
|
||||
err := tx.Insert(a)
|
||||
if err != nil {
|
||||
transaction.Rollback()
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
oldacct, err := GetAccountTx(transaction, a.AccountId, a.UserId)
|
||||
oldacct, err := GetAccountTx(tx, a.AccountId, a.UserId)
|
||||
if err != nil {
|
||||
transaction.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
a.AccountVersion = oldacct.AccountVersion + 1
|
||||
|
||||
count, err := transaction.Update(a)
|
||||
count, err := tx.Update(a)
|
||||
if err != nil {
|
||||
transaction.Rollback()
|
||||
return err
|
||||
}
|
||||
if count != 1 {
|
||||
transaction.Rollback()
|
||||
return errors.New("Updated more than one account")
|
||||
}
|
||||
}
|
||||
|
||||
err = transaction.Commit()
|
||||
if err != nil {
|
||||
transaction.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func InsertAccount(db *DB, a *Account) error {
|
||||
return insertUpdateAccount(db, a, true)
|
||||
func InsertAccount(tx *Tx, a *Account) error {
|
||||
return insertUpdateAccount(tx, a, true)
|
||||
}
|
||||
|
||||
func UpdateAccount(db *DB, a *Account) error {
|
||||
return insertUpdateAccount(db, a, false)
|
||||
func UpdateAccount(tx *Tx, a *Account) error {
|
||||
return insertUpdateAccount(tx, a, false)
|
||||
}
|
||||
|
||||
func DeleteAccount(db *DB, a *Account) error {
|
||||
transaction, err := db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
func DeleteAccount(tx *Tx, a *Account) error {
|
||||
if a.ParentAccountId != -1 {
|
||||
// Re-parent splits to this account's parent account if this account isn't a root account
|
||||
_, err = transaction.Exec("UPDATE splits SET AccountId=? WHERE AccountId=?", a.ParentAccountId, a.AccountId)
|
||||
_, err := tx.Exec("UPDATE splits SET AccountId=? WHERE AccountId=?", a.ParentAccountId, a.AccountId)
|
||||
if err != nil {
|
||||
transaction.Rollback()
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Delete splits if this account is a root account
|
||||
_, err = transaction.Exec("DELETE FROM splits WHERE AccountId=?", a.AccountId)
|
||||
_, err := tx.Exec("DELETE FROM splits WHERE AccountId=?", a.AccountId)
|
||||
if err != nil {
|
||||
transaction.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Re-parent child accounts to this account's parent account
|
||||
_, err = transaction.Exec("UPDATE accounts SET ParentAccountId=? WHERE ParentAccountId=?", a.ParentAccountId, a.AccountId)
|
||||
_, err := tx.Exec("UPDATE accounts SET ParentAccountId=? WHERE ParentAccountId=?", a.ParentAccountId, a.AccountId)
|
||||
if err != nil {
|
||||
transaction.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
count, err := transaction.Delete(a)
|
||||
count, err := tx.Delete(a)
|
||||
if err != nil {
|
||||
transaction.Rollback()
|
||||
return err
|
||||
}
|
||||
if count != 1 {
|
||||
transaction.Rollback()
|
||||
return errors.New("Was going to delete more than one account")
|
||||
}
|
||||
|
||||
err = transaction.Commit()
|
||||
if err != nil {
|
||||
transaction.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func AccountHandler(w http.ResponseWriter, r *http.Request, db *DB) {
|
||||
user, err := GetUserFromSession(db, r)
|
||||
func AccountHandler(r *http.Request, tx *Tx) ResponseWriterWriter {
|
||||
user, err := GetUserFromSession(tx, r)
|
||||
if err != nil {
|
||||
WriteError(w, 1 /*Not Signed In*/)
|
||||
return
|
||||
return NewError(1 /*Not Signed In*/)
|
||||
}
|
||||
|
||||
if r.Method == "POST" {
|
||||
@ -439,59 +404,46 @@ func AccountHandler(w http.ResponseWriter, r *http.Request, db *DB) {
|
||||
n, err := GetURLPieces(r.URL.Path, "/account/%d/import/%s", &accountid, &importtype)
|
||||
|
||||
if err != nil || n != 2 {
|
||||
WriteError(w, 999 /*Internal Error*/)
|
||||
log.Print(err)
|
||||
return
|
||||
return NewError(999 /*Internal Error*/)
|
||||
}
|
||||
AccountImportHandler(db, w, r, user, accountid, importtype)
|
||||
return
|
||||
return AccountImportHandler(tx, r, user, accountid, importtype)
|
||||
}
|
||||
|
||||
account_json := r.PostFormValue("account")
|
||||
if account_json == "" {
|
||||
WriteError(w, 3 /*Invalid Request*/)
|
||||
return
|
||||
return NewError(3 /*Invalid Request*/)
|
||||
}
|
||||
|
||||
var account Account
|
||||
err := account.Read(account_json)
|
||||
if err != nil {
|
||||
WriteError(w, 3 /*Invalid Request*/)
|
||||
return
|
||||
return NewError(3 /*Invalid Request*/)
|
||||
}
|
||||
account.AccountId = -1
|
||||
account.UserId = user.UserId
|
||||
account.AccountVersion = 0
|
||||
|
||||
security, err := GetSecurity(db, account.SecurityId, user.UserId)
|
||||
security, err := GetSecurity(tx, account.SecurityId, user.UserId)
|
||||
if err != nil {
|
||||
WriteError(w, 999 /*Internal Error*/)
|
||||
log.Print(err)
|
||||
return
|
||||
return NewError(999 /*Internal Error*/)
|
||||
}
|
||||
if security == nil {
|
||||
WriteError(w, 3 /*Invalid Request*/)
|
||||
return
|
||||
return NewError(3 /*Invalid Request*/)
|
||||
}
|
||||
|
||||
err = InsertAccount(db, &account)
|
||||
err = InsertAccount(tx, &account)
|
||||
if err != nil {
|
||||
if _, ok := err.(ParentAccountMissingError); ok {
|
||||
WriteError(w, 3 /*Invalid Request*/)
|
||||
return NewError(3 /*Invalid Request*/)
|
||||
} else {
|
||||
WriteError(w, 999 /*Internal Error*/)
|
||||
log.Print(err)
|
||||
return NewError(999 /*Internal Error*/)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(201 /*Created*/)
|
||||
err = account.Write(w)
|
||||
if err != nil {
|
||||
WriteError(w, 999 /*Internal Error*/)
|
||||
log.Print(err)
|
||||
return
|
||||
}
|
||||
return ResponseWrapper{201, &account}
|
||||
} else if r.Method == "GET" {
|
||||
var accountid int64
|
||||
n, err := GetURLPieces(r.URL.Path, "/account/%d", &accountid)
|
||||
@ -499,112 +451,86 @@ func AccountHandler(w http.ResponseWriter, r *http.Request, db *DB) {
|
||||
if err != nil || n != 1 {
|
||||
//Return all Accounts
|
||||
var al AccountList
|
||||
accounts, err := GetAccounts(db, user.UserId)
|
||||
accounts, err := GetAccounts(tx, user.UserId)
|
||||
if err != nil {
|
||||
WriteError(w, 999 /*Internal Error*/)
|
||||
log.Print(err)
|
||||
return
|
||||
return NewError(999 /*Internal Error*/)
|
||||
}
|
||||
al.Accounts = accounts
|
||||
err = (&al).Write(w)
|
||||
if err != nil {
|
||||
WriteError(w, 999 /*Internal Error*/)
|
||||
log.Print(err)
|
||||
return
|
||||
}
|
||||
return &al
|
||||
} else {
|
||||
// if URL looks like /account/[0-9]+/transactions, use the account
|
||||
// transaction handler
|
||||
if accountTransactionsRE.MatchString(r.URL.Path) {
|
||||
AccountTransactionsHandler(db, w, r, user, accountid)
|
||||
return
|
||||
return AccountTransactionsHandler(tx, r, user, accountid)
|
||||
}
|
||||
|
||||
// Return Account with this Id
|
||||
account, err := GetAccount(db, accountid, user.UserId)
|
||||
account, err := GetAccount(tx, accountid, user.UserId)
|
||||
if err != nil {
|
||||
WriteError(w, 3 /*Invalid Request*/)
|
||||
return
|
||||
return NewError(3 /*Invalid Request*/)
|
||||
}
|
||||
|
||||
err = account.Write(w)
|
||||
if err != nil {
|
||||
WriteError(w, 999 /*Internal Error*/)
|
||||
log.Print(err)
|
||||
return
|
||||
}
|
||||
return account
|
||||
}
|
||||
} else {
|
||||
accountid, err := GetURLID(r.URL.Path)
|
||||
if err != nil {
|
||||
WriteError(w, 3 /*Invalid Request*/)
|
||||
return
|
||||
return NewError(3 /*Invalid Request*/)
|
||||
}
|
||||
if r.Method == "PUT" {
|
||||
account_json := r.PostFormValue("account")
|
||||
if account_json == "" {
|
||||
WriteError(w, 3 /*Invalid Request*/)
|
||||
return
|
||||
return NewError(3 /*Invalid Request*/)
|
||||
}
|
||||
|
||||
var account Account
|
||||
err := account.Read(account_json)
|
||||
if err != nil || account.AccountId != accountid {
|
||||
WriteError(w, 3 /*Invalid Request*/)
|
||||
return
|
||||
return NewError(3 /*Invalid Request*/)
|
||||
}
|
||||
account.UserId = user.UserId
|
||||
|
||||
security, err := GetSecurity(db, account.SecurityId, user.UserId)
|
||||
security, err := GetSecurity(tx, account.SecurityId, user.UserId)
|
||||
if err != nil {
|
||||
WriteError(w, 999 /*Internal Error*/)
|
||||
log.Print(err)
|
||||
return
|
||||
return NewError(999 /*Internal Error*/)
|
||||
}
|
||||
if security == nil {
|
||||
WriteError(w, 3 /*Invalid Request*/)
|
||||
return
|
||||
return NewError(3 /*Invalid Request*/)
|
||||
}
|
||||
|
||||
if account.ParentAccountId == account.AccountId {
|
||||
WriteError(w, 3 /*Invalid Request*/)
|
||||
return
|
||||
return NewError(3 /*Invalid Request*/)
|
||||
}
|
||||
|
||||
err = UpdateAccount(db, &account)
|
||||
err = UpdateAccount(tx, &account)
|
||||
if err != nil {
|
||||
if _, ok := err.(ParentAccountMissingError); ok {
|
||||
WriteError(w, 3 /*Invalid Request*/)
|
||||
return NewError(3 /*Invalid Request*/)
|
||||
} else if _, ok := err.(CircularAccountsError); ok {
|
||||
WriteError(w, 3 /*Invalid Request*/)
|
||||
return NewError(3 /*Invalid Request*/)
|
||||
} else {
|
||||
WriteError(w, 999 /*Internal Error*/)
|
||||
log.Print(err)
|
||||
return NewError(999 /*Internal Error*/)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
err = account.Write(w)
|
||||
if err != nil {
|
||||
WriteError(w, 999 /*Internal Error*/)
|
||||
log.Print(err)
|
||||
return
|
||||
}
|
||||
return &account
|
||||
} else if r.Method == "DELETE" {
|
||||
account, err := GetAccount(db, accountid, user.UserId)
|
||||
account, err := GetAccount(tx, accountid, user.UserId)
|
||||
if err != nil {
|
||||
WriteError(w, 3 /*Invalid Request*/)
|
||||
return
|
||||
return NewError(3 /*Invalid Request*/)
|
||||
}
|
||||
|
||||
err = DeleteAccount(db, account)
|
||||
err = DeleteAccount(tx, account)
|
||||
if err != nil {
|
||||
WriteError(w, 999 /*Internal Error*/)
|
||||
log.Print(err)
|
||||
return
|
||||
return NewError(999 /*Internal Error*/)
|
||||
}
|
||||
|
||||
WriteSuccess(w)
|
||||
return SuccessWriter{}
|
||||
}
|
||||
}
|
||||
return NewError(3 /*Invalid Request*/)
|
||||
}
|
||||
|
Reference in New Issue
Block a user