Add category filtering #37
@ -59,7 +59,7 @@ func (server *ServerImpl) GetTransactions(ctx echo.Context, params GetTransactio
|
|||||||
offset = int(*params.Offset)
|
offset = int(*params.Offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
transactions, err := server.Dal.Transactions(limit, offset)
|
transactions, err := server.Dal.Transactions(limit, offset, params.Category)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -146,6 +146,17 @@ func TestServerImpl_GetTransactions(t *testing.T) {
|
|||||||
false,
|
false,
|
||||||
entity.Transactions{{Id: 1, Date: time.Now(), Description: "desc#1", Value: decimal.New(0, 0)}},
|
entity.Transactions{{Id: 1, Date: time.Now(), Description: "desc#1", Value: decimal.New(0, 0)}},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"200+category",
|
||||||
|
fields{mock_dal},
|
||||||
|
args{GetTransactionsParams{
|
||||||
|
Limit: golang.Ptr[int32](100),
|
||||||
|
Offset: golang.Ptr[int32](0),
|
||||||
|
Category: golang.Ptr("cat"),
|
||||||
|
}},
|
||||||
|
false,
|
||||||
|
entity.Transactions{{Id: 1, Date: time.Now(), Description: "desc#1", Value: decimal.New(0, 0), Category: golang.Ptr("cat")}},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"204",
|
"204",
|
||||||
fields{mock_dal},
|
fields{mock_dal},
|
||||||
@ -164,13 +175,16 @@ func TestServerImpl_GetTransactions(t *testing.T) {
|
|||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
ctx := echo.New().NewContext(req, rec)
|
ctx := echo.New().NewContext(req, rec)
|
||||||
|
|
||||||
mock_dal.EXPECT().Transactions(int(*tt.args.params.Limit), int(*tt.args.params.Offset)).Return(tt.mocks, nil).Times(1)
|
mock_dal.EXPECT().
|
||||||
|
Transactions(int(*tt.args.params.Limit), int(*tt.args.params.Offset), tt.args.params.Category).
|
||||||
|
Return(tt.mocks, nil).
|
||||||
|
Times(1)
|
||||||
|
|
||||||
if err := pf.GetTransactions(ctx, tt.args.params); (err != nil) != tt.wantErr {
|
if err := pf.GetTransactions(ctx, tt.args.params); (err != nil) != tt.wantErr {
|
||||||
t.Errorf("ServerImpl.GetTransactions() error = %v,", err)
|
t.Errorf("ServerImpl.GetTransactions() error = %v,", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
code, _ := strconv.Atoi(tt.name)
|
code, _ := strconv.Atoi(strings.Split(tt.name, "+")[0])
|
||||||
assert.Equal(t, code, rec.Code)
|
assert.Equal(t, code, rec.Code)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import "git.rosemyrtle.work/personal-finance/server/internal/entity"
|
|||||||
|
|
||||||
type DAL interface {
|
type DAL interface {
|
||||||
Transaction(transactionId int64) (*entity.Transaction, error)
|
Transaction(transactionId int64) (*entity.Transaction, error)
|
||||||
Transactions(limit, offset int) (entity.Transactions, error)
|
Transactions(limit, offset int, category *string) (entity.Transactions, error)
|
||||||
InsertTransaction(entity.Transaction) (entity.Transaction, error)
|
InsertTransaction(entity.Transaction) (entity.Transaction, error)
|
||||||
UpdateTransaction(entity.TransactionId, *entity.CategoryName) (bool, error)
|
UpdateTransaction(entity.TransactionId, *entity.CategoryName) (bool, error)
|
||||||
TransactionExists(uint64) (bool, error)
|
TransactionExists(uint64) (bool, error)
|
||||||
|
|||||||
@ -41,8 +41,8 @@ func (dal *DalImpl) Transaction(transactionId int64) (*entity.Transaction, error
|
|||||||
return &transactions[0], nil
|
return &transactions[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dal *DalImpl) Transactions(limit, offset int) (entity.Transactions, error) {
|
func (dal *DalImpl) Transactions(limit, offset int, category *string) (entity.Transactions, error) {
|
||||||
log.Print("DAL::Transactions", "limit", limit, "offset", offset)
|
log.Print("DAL::Transactions", "limit", limit, "offset", offset, "category", category)
|
||||||
|
|
||||||
if dal.Db == nil {
|
if dal.Db == nil {
|
||||||
log.Panic("database not available")
|
log.Panic("database not available")
|
||||||
@ -53,12 +53,21 @@ func (dal *DalImpl) Transactions(limit, offset int) (entity.Transactions, error)
|
|||||||
FROM pfbudget.transactions t
|
FROM pfbudget.transactions t
|
||||||
LEFT JOIN pfbudget.transactions_categorized tc
|
LEFT JOIN pfbudget.transactions_categorized tc
|
||||||
ON t.id = tc.id
|
ON t.id = tc.id
|
||||||
|
`
|
||||||
|
args := []any{limit, offset}
|
||||||
|
|
||||||
|
if category != nil {
|
||||||
|
stmt += `WHERE tc.name SIMILAR TO '%' || $3 || '%'`
|
||||||
|
args = append(args, *category)
|
||||||
|
}
|
||||||
|
stmt += `
|
||||||
ORDER BY t.date DESC
|
ORDER BY t.date DESC
|
||||||
LIMIT $1
|
LIMIT $1
|
||||||
OFFSET $2
|
OFFSET $2
|
||||||
`
|
`
|
||||||
|
log.Print(stmt, args)
|
||||||
|
|
||||||
rows, err := dal.Db.Query(stmt, limit, offset)
|
rows, err := dal.Db.Query(stmt, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return entity.Transactions{}, err
|
return entity.Transactions{}, err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -99,9 +99,14 @@ func TestDalImpl_Transactions(t *testing.T) {
|
|||||||
type fields struct {
|
type fields struct {
|
||||||
Db *sql.DB
|
Db *sql.DB
|
||||||
}
|
}
|
||||||
|
type args struct {
|
||||||
|
limit, offset int
|
||||||
|
category *string
|
||||||
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
fields fields
|
fields fields
|
||||||
|
args args
|
||||||
mocks [][]driver.Value
|
mocks [][]driver.Value
|
||||||
want entity.Transactions
|
want entity.Transactions
|
||||||
wantErr bool
|
wantErr bool
|
||||||
@ -109,6 +114,7 @@ func TestDalImpl_Transactions(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"SelectTransactions",
|
"SelectTransactions",
|
||||||
fields{db},
|
fields{db},
|
||||||
|
args{limit: 30, offset: 0, category: nil},
|
||||||
[][]driver.Value{
|
[][]driver.Value{
|
||||||
{1, date, "income", 1000, nil},
|
{1, date, "income", 1000, nil},
|
||||||
{2, date, "expense", -10.50, nil},
|
{2, date, "expense", -10.50, nil},
|
||||||
@ -122,6 +128,7 @@ func TestDalImpl_Transactions(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"SelectTransactionsWithCategory",
|
"SelectTransactionsWithCategory",
|
||||||
fields{db},
|
fields{db},
|
||||||
|
args{limit: 30, offset: 0, category: golang.Ptr("C1")},
|
||||||
[][]driver.Value{
|
[][]driver.Value{
|
||||||
{1, date, "income", 1000, "C1"},
|
{1, date, "income", 1000, "C1"},
|
||||||
{2, date, "expense", -10.50, nil},
|
{2, date, "expense", -10.50, nil},
|
||||||
@ -133,7 +140,7 @@ func TestDalImpl_Transactions(t *testing.T) {
|
|||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"SelectNoTransactions", fields{db}, nil, nil, false,
|
"SelectNoTransactions", fields{db}, args{limit: 30, offset: 0, category: nil}, nil, nil, false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
@ -142,23 +149,31 @@ func TestDalImpl_Transactions(t *testing.T) {
|
|||||||
Db: tt.fields.Db,
|
Db: tt.fields.Db,
|
||||||
}
|
}
|
||||||
|
|
||||||
limit, offset := 0, 0
|
args := []driver.Value{tt.args.limit, tt.args.offset}
|
||||||
|
stmt := `
|
||||||
mock.
|
|
||||||
ExpectQuery(`
|
|
||||||
^SELECT \w+\.id, \w+\.date, \w+\.description, \w+\.amount, \w+\.name
|
^SELECT \w+\.id, \w+\.date, \w+\.description, \w+\.amount, \w+\.name
|
||||||
FROM \w+\.transactions \w+
|
FROM \w+\.transactions \w+
|
||||||
LEFT JOIN \w+\.transactions_categorized \w+
|
LEFT JOIN \w+\.transactions_categorized \w+
|
||||||
ON \w+\.id = \w+\.id
|
ON \w+\.id = \w+\.id
|
||||||
|
`
|
||||||
|
if tt.args.category != nil {
|
||||||
|
stmt += `WHERE \w+\.name SIMILAR TO '%' || \$3 || '%'`
|
||||||
|
args = append(args, *tt.args.category)
|
||||||
|
}
|
||||||
|
stmt += `
|
||||||
ORDER BY \w+\.date DESC
|
ORDER BY \w+\.date DESC
|
||||||
LIMIT \$1
|
LIMIT \$1
|
||||||
OFFSET \$2`).
|
OFFSET \$2
|
||||||
WithArgs(limit, offset).
|
`
|
||||||
|
|
||||||
|
mock.
|
||||||
|
ExpectQuery(stmt).
|
||||||
|
WithArgs(args...).
|
||||||
WillReturnRows(
|
WillReturnRows(
|
||||||
mock.NewRows([]string{"id", "date", "description", "amount", "category"}).AddRows(tt.mocks...),
|
mock.NewRows([]string{"id", "date", "description", "amount", "category"}).AddRows(tt.mocks...),
|
||||||
)
|
)
|
||||||
|
|
||||||
got, err := dal.Transactions(limit, offset)
|
got, err := dal.Transactions(tt.args.limit, tt.args.offset, tt.args.category)
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
t.Errorf("DalImpl.Transactions() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("DalImpl.Transactions() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
return
|
return
|
||||||
|
|||||||
@ -131,18 +131,18 @@ func (mr *MockDALMockRecorder) TransactionExists(arg0 any) *gomock.Call {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Transactions mocks base method.
|
// Transactions mocks base method.
|
||||||
func (m *MockDAL) Transactions(limit, offset int) ([]entity.Transaction, error) {
|
func (m *MockDAL) Transactions(limit, offset int, category *string) ([]entity.Transaction, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "Transactions", limit, offset)
|
ret := m.ctrl.Call(m, "Transactions", limit, offset, category)
|
||||||
ret0, _ := ret[0].([]entity.Transaction)
|
ret0, _ := ret[0].([]entity.Transaction)
|
||||||
ret1, _ := ret[1].(error)
|
ret1, _ := ret[1].(error)
|
||||||
return ret0, ret1
|
return ret0, ret1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transactions indicates an expected call of Transactions.
|
// Transactions indicates an expected call of Transactions.
|
||||||
func (mr *MockDALMockRecorder) Transactions(limit, offset any) *gomock.Call {
|
func (mr *MockDALMockRecorder) Transactions(limit, offset, category any) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Transactions", reflect.TypeOf((*MockDAL)(nil).Transactions), limit, offset)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Transactions", reflect.TypeOf((*MockDAL)(nil).Transactions), limit, offset, category)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateTransaction mocks base method.
|
// UpdateTransaction mocks base method.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user