Skip to content

Commit

Permalink
feat: return transaction state and allow fetching unpaid transactions…
Browse files Browse the repository at this point in the history
… by state (#835)

* feat: return transaction state and allow fetching unpaid transactions by state

* chore: add list transactions tests
  • Loading branch information
rolznz authored Dec 3, 2024
1 parent faf9063 commit 911d5f8
Show file tree
Hide file tree
Showing 4 changed files with 274 additions and 15 deletions.
17 changes: 9 additions & 8 deletions nip47/controllers/list_transactions_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ import (
)

type listTransactionsParams struct {
From uint64 `json:"from,omitempty"`
Until uint64 `json:"until,omitempty"`
Limit uint64 `json:"limit,omitempty"`
Offset uint64 `json:"offset,omitempty"`
Unpaid bool `json:"unpaid,omitempty"`
Type string `json:"type,omitempty"`
From uint64 `json:"from,omitempty"`
Until uint64 `json:"until,omitempty"`
Limit uint64 `json:"limit,omitempty"`
Offset uint64 `json:"offset,omitempty"`
Unpaid bool `json:"unpaid,omitempty"`
UnpaidOutgoing bool `json:"unpaid_outgoing,omitempty"`
UnpaidIncoming bool `json:"unpaid_incoming,omitempty"`
Type string `json:"type,omitempty"`
}

type listTransactionsResponse struct {
Expand Down Expand Up @@ -48,8 +50,7 @@ func (controller *nip47Controller) HandleListTransactionsEvent(ctx context.Conte
transactionType = &listParams.Type
}

// TODO: listParams.Unpaid needs to be updated to support ability to fetch only unpaid outgoing transactions
dbTransactions, err := controller.transactionsService.ListTransactions(ctx, listParams.From, listParams.Until, limit, listParams.Offset, listParams.Unpaid, listParams.Unpaid, transactionType, controller.lnClient, &appId, false)
dbTransactions, err := controller.transactionsService.ListTransactions(ctx, listParams.From, listParams.Until, limit, listParams.Offset, listParams.Unpaid || listParams.UnpaidOutgoing, listParams.Unpaid || listParams.UnpaidIncoming, transactionType, controller.lnClient, &appId, false)
if err != nil {
logger.Logger.WithFields(logrus.Fields{
"params": listParams,
Expand Down
269 changes: 262 additions & 7 deletions nip47/controllers/list_transactions_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@ import (
"github.com/getAlby/hub/transactions"
)

const nip47ListTransactionsJson = `
func TestHandleListTransactionsEvent(t *testing.T) {
ctx := context.TODO()
defer tests.RemoveTestService()
svc, err := tests.CreateTestService()
require.NoError(t, err)

const nip47ListTransactionsJson = `
{
"method": "list_transactions",
"params": {
Expand All @@ -31,12 +37,6 @@ const nip47ListTransactionsJson = `
}
`

func TestHandleListTransactionsEvent(t *testing.T) {
ctx := context.TODO()
defer tests.RemoveTestService()
svc, err := tests.CreateTestService()
require.NoError(t, err)

nip47Request := &models.Request{}
err = json.Unmarshal([]byte(nip47ListTransactionsJson), nip47Request)
assert.NoError(t, err)
Expand Down Expand Up @@ -93,6 +93,261 @@ func TestHandleListTransactionsEvent(t *testing.T) {
assert.Equal(t, tests.MockLNClientTransactions[0].Amount, transaction.Amount)
assert.Equal(t, tests.MockLNClientTransactions[0].FeesPaid, transaction.FeesPaid)
assert.Equal(t, tests.MockLNClientTransactions[0].SettledAt, transaction.SettledAt)
assert.Equal(t, "settled", transaction.State)
}

func TestHandleListTransactionsEvent_UnpaidOutgoingOnly(t *testing.T) {
ctx := context.TODO()
defer tests.RemoveTestService()
svc, err := tests.CreateTestService()
require.NoError(t, err)

const nip47ListTransactionsJson = `
{
"method": "list_transactions",
"params": {
"from": 0,
"until": 0,
"limit": 10,
"offset": 0,
"unpaid_outgoing": true
}
}
`

nip47Request := &models.Request{}
err = json.Unmarshal([]byte(nip47ListTransactionsJson), nip47Request)
assert.NoError(t, err)

app, _, err := tests.CreateApp(svc)
assert.NoError(t, err)

dbRequestEvent := &db.RequestEvent{
AppId: &app.ID,
}
err = svc.DB.Create(&dbRequestEvent).Error
assert.NoError(t, err)

err = svc.DB.Create(&db.Transaction{
Type: constants.TRANSACTION_TYPE_INCOMING,
State: constants.TRANSACTION_STATE_PENDING,
}).Error
assert.NoError(t, err)

err = svc.DB.Create(&db.Transaction{
Type: constants.TRANSACTION_TYPE_OUTGOING,
State: constants.TRANSACTION_STATE_PENDING,
}).Error
assert.NoError(t, err)

var publishedResponse *models.Response

publishResponse := func(response *models.Response, tags nostr.Tags) {
publishedResponse = response
}

permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
HandleListTransactionsEvent(ctx, nip47Request, dbRequestEvent.ID, *dbRequestEvent.AppId, publishResponse)

assert.Nil(t, publishedResponse.Error)

assert.Equal(t, 1, len(publishedResponse.Result.(*listTransactionsResponse).Transactions))
transaction := publishedResponse.Result.(*listTransactionsResponse).Transactions[0]
assert.Equal(t, constants.TRANSACTION_TYPE_OUTGOING, transaction.Type)
}

func TestHandleListTransactionsEvent_UnpaidIncomingOnly(t *testing.T) {
ctx := context.TODO()
defer tests.RemoveTestService()
svc, err := tests.CreateTestService()
require.NoError(t, err)

const nip47ListTransactionsJson = `
{
"method": "list_transactions",
"params": {
"from": 0,
"until": 0,
"limit": 10,
"offset": 0,
"unpaid_incoming": true
}
}
`

nip47Request := &models.Request{}
err = json.Unmarshal([]byte(nip47ListTransactionsJson), nip47Request)
assert.NoError(t, err)

app, _, err := tests.CreateApp(svc)
assert.NoError(t, err)

dbRequestEvent := &db.RequestEvent{
AppId: &app.ID,
}
err = svc.DB.Create(&dbRequestEvent).Error
assert.NoError(t, err)

err = svc.DB.Create(&db.Transaction{
Type: constants.TRANSACTION_TYPE_INCOMING,
State: constants.TRANSACTION_STATE_PENDING,
}).Error
assert.NoError(t, err)

err = svc.DB.Create(&db.Transaction{
Type: constants.TRANSACTION_TYPE_OUTGOING,
State: constants.TRANSACTION_STATE_PENDING,
}).Error
assert.NoError(t, err)

var publishedResponse *models.Response

publishResponse := func(response *models.Response, tags nostr.Tags) {
publishedResponse = response
}

permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
HandleListTransactionsEvent(ctx, nip47Request, dbRequestEvent.ID, *dbRequestEvent.AppId, publishResponse)

assert.Nil(t, publishedResponse.Error)

assert.Equal(t, 1, len(publishedResponse.Result.(*listTransactionsResponse).Transactions))
transaction := publishedResponse.Result.(*listTransactionsResponse).Transactions[0]
assert.Equal(t, constants.TRANSACTION_TYPE_INCOMING, transaction.Type)
}

func TestHandleListTransactionsEvent_Unpaid(t *testing.T) {
ctx := context.TODO()
defer tests.RemoveTestService()
svc, err := tests.CreateTestService()
require.NoError(t, err)

const nip47ListTransactionsJson = `
{
"method": "list_transactions",
"params": {
"from": 0,
"until": 0,
"limit": 10,
"offset": 0,
"unpaid": true
}
}
`

nip47Request := &models.Request{}
err = json.Unmarshal([]byte(nip47ListTransactionsJson), nip47Request)
assert.NoError(t, err)

app, _, err := tests.CreateApp(svc)
assert.NoError(t, err)

dbRequestEvent := &db.RequestEvent{
AppId: &app.ID,
}
err = svc.DB.Create(&dbRequestEvent).Error
assert.NoError(t, err)

err = svc.DB.Create(&db.Transaction{
Type: constants.TRANSACTION_TYPE_INCOMING,
State: constants.TRANSACTION_STATE_PENDING,
}).Error
assert.NoError(t, err)

err = svc.DB.Create(&db.Transaction{
Type: constants.TRANSACTION_TYPE_OUTGOING,
State: constants.TRANSACTION_STATE_PENDING,
}).Error
assert.NoError(t, err)

var publishedResponse *models.Response

publishResponse := func(response *models.Response, tags nostr.Tags) {
publishedResponse = response
}

permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
HandleListTransactionsEvent(ctx, nip47Request, dbRequestEvent.ID, *dbRequestEvent.AppId, publishResponse)

assert.Nil(t, publishedResponse.Error)

assert.Equal(t, 2, len(publishedResponse.Result.(*listTransactionsResponse).Transactions))
}

func TestHandleListTransactionsEvent_Paid(t *testing.T) {
ctx := context.TODO()
defer tests.RemoveTestService()
svc, err := tests.CreateTestService()
require.NoError(t, err)

const nip47ListTransactionsJson = `
{
"method": "list_transactions",
"params": {
"from": 0,
"until": 0,
"limit": 10,
"offset": 0
}
}
`

nip47Request := &models.Request{}
err = json.Unmarshal([]byte(nip47ListTransactionsJson), nip47Request)
assert.NoError(t, err)

app, _, err := tests.CreateApp(svc)
assert.NoError(t, err)

dbRequestEvent := &db.RequestEvent{
AppId: &app.ID,
}
err = svc.DB.Create(&dbRequestEvent).Error
assert.NoError(t, err)

err = svc.DB.Create(&db.Transaction{
Type: constants.TRANSACTION_TYPE_INCOMING,
State: constants.TRANSACTION_STATE_PENDING,
}).Error
assert.NoError(t, err)

err = svc.DB.Create(&db.Transaction{
Type: constants.TRANSACTION_TYPE_OUTGOING,
State: constants.TRANSACTION_STATE_PENDING,
}).Error
assert.NoError(t, err)

settledPaymentHash := "dummy payment hash"

err = svc.DB.Create(&db.Transaction{
Type: constants.TRANSACTION_TYPE_OUTGOING,
State: constants.TRANSACTION_STATE_SETTLED,
PaymentHash: settledPaymentHash,
}).Error
assert.NoError(t, err)

var publishedResponse *models.Response

publishResponse := func(response *models.Response, tags nostr.Tags) {
publishedResponse = response
}

permissionsSvc := permissions.NewPermissionsService(svc.DB, svc.EventPublisher)
transactionsSvc := transactions.NewTransactionsService(svc.DB, svc.EventPublisher)
NewNip47Controller(svc.LNClient, svc.DB, svc.EventPublisher, permissionsSvc, transactionsSvc).
HandleListTransactionsEvent(ctx, nip47Request, dbRequestEvent.ID, *dbRequestEvent.AppId, publishResponse)

assert.Nil(t, publishedResponse.Error)

assert.Equal(t, 1, len(publishedResponse.Result.(*listTransactionsResponse).Transactions))
transaction := publishedResponse.Result.(*listTransactionsResponse).Transactions[0]
assert.Equal(t, settledPaymentHash, transaction.PaymentHash)
}

// TODO: add tests for pagination args
1 change: 1 addition & 0 deletions nip47/models/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const (

type Transaction struct {
Type string `json:"type"`
State string `json:"state"`
Invoice string `json:"invoice"`
Description string `json:"description"`
DescriptionHash string `json:"description_hash"`
Expand Down
2 changes: 2 additions & 0 deletions nip47/models/transactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package models

import (
"encoding/json"
"strings"

"github.com/getAlby/hub/logger"
"github.com/getAlby/hub/transactions"
Expand Down Expand Up @@ -36,6 +37,7 @@ func ToNip47Transaction(transaction *transactions.Transaction) *Transaction {

return &Transaction{
Type: transaction.Type,
State: strings.ToLower(transaction.State),
Invoice: transaction.PaymentRequest,
Description: transaction.Description,
DescriptionHash: transaction.DescriptionHash,
Expand Down

0 comments on commit 911d5f8

Please sign in to comment.