diff --git a/db/db.go b/db/db.go index d99e18e5e..44a32b643 100644 --- a/db/db.go +++ b/db/db.go @@ -1724,9 +1724,18 @@ func (db database) AddUserInvoiceData(userData UserInvoiceData) UserInvoiceData func (db database) ProcessAddInvoice(invoice NewInvoiceList, userData UserInvoiceData) error { tx := db.db.Begin() - var err error + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + if err = tx.Error; err != nil { + return err + } + if err = tx.Create(&invoice).Error; err != nil { tx.Rollback() } @@ -1740,9 +1749,18 @@ func (db database) ProcessAddInvoice(invoice NewInvoiceList, userData UserInvoic func (db database) ProcessBudgetInvoice(paymentHistory NewPaymentHistory, newInvoice NewInvoiceList) error { tx := db.db.Begin() - var err error + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + if err = tx.Error; err != nil { + return err + } + if err = tx.Create(&paymentHistory).Error; err != nil { tx.Rollback() } diff --git a/db/interface.go b/db/interface.go index c86d68760..444901279 100644 --- a/db/interface.go +++ b/db/interface.go @@ -109,6 +109,7 @@ type Database interface { AddAndUpdateBudget(invoice NewInvoiceList) NewPaymentHistory WithdrawBudget(sender_pubkey string, workspace_uuid string, amount uint) AddPaymentHistory(payment NewPaymentHistory) NewPaymentHistory + ProcessBountyPayment(payment NewPaymentHistory, bounty NewBounty) error GetPaymentHistory(workspace_uuid string, r *http.Request) []NewPaymentHistory GetInvoice(payment_request string) NewInvoiceList GetWorkspaceInvoices(workspace_uuid string) []NewInvoiceList diff --git a/db/workspaces.go b/db/workspaces.go index 085e5017b..208f1efd6 100644 --- a/db/workspaces.go +++ b/db/workspaces.go @@ -255,11 +255,21 @@ func (db database) ProcessUpdateBudget(invoice NewInvoiceList) error { // Start db transaction tx := db.db.Begin() + var err error + + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + if err = tx.Error; err != nil { + return err + } + created := invoice.Created workspace_uuid := invoice.WorkspaceUuid - var err error - // Get payment history and update budget paymentHistory := db.GetPaymentHistoryByCreated(created, workspace_uuid) if paymentHistory.WorkspaceUuid != "" && paymentHistory.Amount != 0 { @@ -341,6 +351,16 @@ func (db database) WithdrawBudget(sender_pubkey string, workspace_uuid string, a tx := db.db.Begin() var err error + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + if err = tx.Error; err != nil { + return + } + // get Workspace budget and add payment to total budget WorkspaceBudget := db.GetWorkspaceBudget(workspace_uuid) totalBudget := WorkspaceBudget.TotalBudget @@ -389,6 +409,48 @@ func (db database) AddPaymentHistory(payment NewPaymentHistory) NewPaymentHistor return payment } +func (db database) ProcessBountyPayment(payment NewPaymentHistory, bounty NewBounty) error { + tx := db.db.Begin() + var err error + + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + if err = tx.Error; err != nil { + return err + } + + // add to payment history + if err = tx.Create(&payment).Error; err != nil { + tx.Rollback() + return err + } + + // get Workspace budget and subtract payment from total budget + WorkspaceBudget := db.GetWorkspaceBudget(payment.WorkspaceUuid) + totalBudget := WorkspaceBudget.TotalBudget + + // update budget + WorkspaceBudget.TotalBudget = totalBudget - payment.Amount + if err = tx.Model(&NewBountyBudget{}).Where("workspace_uuid = ?", payment.WorkspaceUuid).Updates(map[string]interface{}{ + "total_budget": WorkspaceBudget.TotalBudget, + }).Error; err != nil { + tx.Rollback() + return err + } + + // updatge bounty status + if err = tx.Where("created", bounty.Created).Updates(&bounty).Error; err != nil { + tx.Rollback() + return err + } + + return tx.Commit().Error +} + func (db database) GetPaymentHistory(workspace_uuid string, r *http.Request) []NewPaymentHistory { payment := []NewPaymentHistory{} @@ -445,6 +507,16 @@ func (db database) ProcessDeleteWorkspace(workspace_uuid string) error { tx := db.db.Begin() var err error + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + if err = tx.Error; err != nil { + return err + } + updates := map[string]interface{}{ "website": "", "github": "", diff --git a/handlers/bounty.go b/handlers/bounty.go index 32c054f3d..a141d9f65 100644 --- a/handlers/bounty.go +++ b/handlers/bounty.go @@ -527,6 +527,12 @@ func (h *bountyHandler) MakeBountyPayment(w http.ResponseWriter, r *http.Request request := db.BountyPayRequest{} body, err := io.ReadAll(r.Body) r.Body.Close() + if err != nil { + fmt.Println("[read body]", err) + w.WriteHeader(http.StatusNotAcceptable) + h.m.Unlock() + return + } err = json.Unmarshal(body, &request) if err != nil { @@ -557,6 +563,13 @@ func (h *bountyHandler) MakeBountyPayment(w http.ResponseWriter, r *http.Request defer res.Body.Close() body, err = io.ReadAll(res.Body) + if err != nil { + fmt.Println("[read body]", err) + w.WriteHeader(http.StatusNotAcceptable) + h.m.Unlock() + return + } + msg := make(map[string]interface{}) // payment is successful add to payment history @@ -566,7 +579,15 @@ func (h *bountyHandler) MakeBountyPayment(w http.ResponseWriter, r *http.Request keysendRes := db.KeysendSuccess{} err = json.Unmarshal(body, &keysendRes) + if err != nil { + fmt.Println("[Unmarshal]", err) + w.WriteHeader(http.StatusNotAcceptable) + h.m.Unlock() + return + } + now := time.Now() + paymentHistory := db.NewPaymentHistory{ Amount: amount, SenderPubKey: pubKeyFromAuth, @@ -578,13 +599,13 @@ func (h *bountyHandler) MakeBountyPayment(w http.ResponseWriter, r *http.Request Status: true, PaymentType: "payment", } - h.db.AddPaymentHistory(paymentHistory) bounty.Paid = true bounty.PaidDate = &now bounty.Completed = true bounty.CompletionDate = &now - h.db.UpdateBounty(bounty) + + h.db.ProcessBountyPayment(paymentHistory, bounty) msg["msg"] = "keysend_success" msg["invoice"] = "" diff --git a/handlers/bounty_test.go b/handlers/bounty_test.go index 38c25f386..baefbb08a 100644 --- a/handlers/bounty_test.go +++ b/handlers/bounty_test.go @@ -106,7 +106,6 @@ func TestCreateOrEditBounty(t *testing.T) { rr := httptest.NewRecorder() handler := http.HandlerFunc(bHandler.CreateOrEditBounty) - existingBounty := db.NewBounty{ ID: 1, Type: "coding", @@ -1325,28 +1324,10 @@ func TestMakeBountyPayment(t *testing.T) { bHandler.getSocketConnections = mockGetSocketConnections bHandler.userHasAccess = mockUserHasAccessTrue - now := time.Now() - expectedBounty := db.NewBounty{ - ID: bountyID, - OrgUuid: "org-1", - WorkspaceUuid: "work-1", - Assignee: "assignee-1", - Price: uint(1000), - Paid: true, - PaidDate: &now, - CompletionDate: &now, - } - mockDb.On("GetBounty", bountyID).Return(bounty, nil) mockDb.On("GetWorkspaceBudget", bounty.WorkspaceUuid).Return(db.NewBountyBudget{TotalBudget: 2000}, nil) mockDb.On("GetPersonByPubkey", bounty.Assignee).Return(db.Person{OwnerPubKey: "assignee-1", OwnerRouteHint: "OwnerRouteHint"}, nil) - mockDb.On("AddPaymentHistory", mock.AnythingOfType("db.NewPaymentHistory")).Return(db.NewPaymentHistory{ID: 1}) - mockDb.On("UpdateBounty", mock.AnythingOfType("db.NewBounty")).Run(func(args mock.Arguments) { - updatedBounty := args.Get(0).(db.NewBounty) - assert.True(t, updatedBounty.Paid) - assert.NotNil(t, updatedBounty.PaidDate) - assert.NotNil(t, updatedBounty.CompletionDate) - }).Return(expectedBounty, nil).Once() + mockDb.On("ProcessBountyPayment", mock.AnythingOfType("db.NewPaymentHistory"), mock.AnythingOfType("db.NewBounty")).Return(nil) expectedUrl := fmt.Sprintf("%s/payment", config.RelayUrl) expectedBody := `{"amount": 1000, "destination_key": "assignee-1", "route_hint": "OwnerRouteHint", "text": "memotext added for notification"}` diff --git a/mocks/Database.go b/mocks/Database.go index 7feee5b53..7d9f64afa 100644 --- a/mocks/Database.go +++ b/mocks/Database.go @@ -6403,6 +6403,53 @@ func (_c *Database_ProcessAlerts_Call) RunAndReturn(run func(db.Person)) *Databa return _c } +// ProcessBountyPayment provides a mock function with given fields: payment, bounty +func (_m *Database) ProcessBountyPayment(payment db.NewPaymentHistory, bounty db.NewBounty) error { + ret := _m.Called(payment, bounty) + + if len(ret) == 0 { + panic("no return value specified for ProcessBountyPayment") + } + + var r0 error + if rf, ok := ret.Get(0).(func(db.NewPaymentHistory, db.NewBounty) error); ok { + r0 = rf(payment, bounty) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Database_ProcessBountyPayment_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ProcessBountyPayment' +type Database_ProcessBountyPayment_Call struct { + *mock.Call +} + +// ProcessBountyPayment is a helper method to define mock.On call +// - payment db.NewPaymentHistory +// - bounty db.NewBounty +func (_e *Database_Expecter) ProcessBountyPayment(payment interface{}, bounty interface{}) *Database_ProcessBountyPayment_Call { + return &Database_ProcessBountyPayment_Call{Call: _e.mock.On("ProcessBountyPayment", payment, bounty)} +} + +func (_c *Database_ProcessBountyPayment_Call) Run(run func(payment db.NewPaymentHistory, bounty db.NewBounty)) *Database_ProcessBountyPayment_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(db.NewPaymentHistory), args[1].(db.NewBounty)) + }) + return _c +} + +func (_c *Database_ProcessBountyPayment_Call) Return(_a0 error) *Database_ProcessBountyPayment_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *Database_ProcessBountyPayment_Call) RunAndReturn(run func(db.NewPaymentHistory, db.NewBounty) error) *Database_ProcessBountyPayment_Call { + _c.Call.Return(run) + return _c +} + // ProcessBudgetInvoice provides a mock function with given fields: paymentHistory, newInvoice func (_m *Database) ProcessBudgetInvoice(paymentHistory db.NewPaymentHistory, newInvoice db.NewInvoiceList) error { ret := _m.Called(paymentHistory, newInvoice)