diff --git a/docs/openapi.yaml b/docs/openapi.yaml index 0475dec..dbc32cb 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -36,7 +36,8 @@ paths: schema: type: integer format: int32 - default: -1 + default: 0 + minimum: 0 - name: bank in: query description: ID of the bank diff --git a/internal/api/impl.go b/internal/api/impl.go index 9017165..b643c89 100644 --- a/internal/api/impl.go +++ b/internal/api/impl.go @@ -49,9 +49,19 @@ func (server *ServerImpl) GetBankById(ctx echo.Context, bankId string) error { func (server *ServerImpl) GetTransactions(ctx echo.Context, params GetTransactionsParams) error { log.Print("GetTransactions") - transactions, err := server.Dal.Transactions() + limit := 100 + if params.Limit != nil { + limit = int(*params.Limit) + } + + offset := 0 + if params.Offset != nil { + offset = int(*params.Offset) + } + + transactions, err := server.Dal.Transactions(limit, offset) if err != nil { - return ctx.NoContent(http.StatusInternalServerError) + return err } if len(transactions) == 0 { diff --git a/internal/api/impl_test.go b/internal/api/impl_test.go index a2becef..e2b23ae 100644 --- a/internal/api/impl_test.go +++ b/internal/api/impl_test.go @@ -142,14 +142,14 @@ func TestServerImpl_GetTransactions(t *testing.T) { { "200", fields{mock_dal}, - args{GetTransactionsParams{}}, + args{GetTransactionsParams{Limit: golang.Ptr[int32](100), Offset: golang.Ptr[int32](0)}}, false, entity.Transactions{{Id: 1, Date: time.Now(), Description: "desc#1", Value: decimal.New(0, 0)}}, }, { "204", fields{mock_dal}, - args{GetTransactionsParams{}}, + args{GetTransactionsParams{Limit: golang.Ptr[int32](100), Offset: golang.Ptr[int32](0)}}, false, entity.Transactions{}, }, @@ -164,7 +164,7 @@ func TestServerImpl_GetTransactions(t *testing.T) { rec := httptest.NewRecorder() ctx := echo.New().NewContext(req, rec) - mock_dal.EXPECT().Transactions().Return(tt.mocks, nil).Times(1) + mock_dal.EXPECT().Transactions(int(*tt.args.params.Limit), int(*tt.args.params.Offset)).Return(tt.mocks, nil).Times(1) if err := pf.GetTransactions(ctx, tt.args.params); (err != nil) != tt.wantErr { t.Errorf("ServerImpl.GetTransactions() error = %v,", err) diff --git a/internal/api/server.gen.go b/internal/api/server.gen.go index 5b032be..c035835 100644 --- a/internal/api/server.gen.go +++ b/internal/api/server.gen.go @@ -276,24 +276,24 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/internal/dal/dal.go b/internal/dal/dal.go index 7b7aaa7..7a3a5b9 100644 --- a/internal/dal/dal.go +++ b/internal/dal/dal.go @@ -4,7 +4,7 @@ import "git.rosemyrtle.work/personal-finance/server/internal/entity" type DAL interface { Transaction(transactionId int64) (*entity.Transaction, error) - Transactions() (entity.Transactions, error) + Transactions(limit, offset int) (entity.Transactions, error) InsertTransaction(entity.Transaction) (entity.Transaction, error) UpdateTransaction(entity.TransactionId, *entity.CategoryName) (bool, error) TransactionExists(uint64) (bool, error) diff --git a/internal/dal/impl.go b/internal/dal/impl.go index 4d0879b..aa3b314 100644 --- a/internal/dal/impl.go +++ b/internal/dal/impl.go @@ -41,8 +41,8 @@ func (dal *DalImpl) Transaction(transactionId int64) (*entity.Transaction, error return &transactions[0], nil } -func (dal *DalImpl) Transactions() (entity.Transactions, error) { - log.Print("DAL::Transactions") +func (dal *DalImpl) Transactions(limit, offset int) (entity.Transactions, error) { + log.Print("DAL::Transactions", "limit", limit, "offset", offset) if dal.Db == nil { log.Panic("database not available") @@ -53,9 +53,12 @@ func (dal *DalImpl) Transactions() (entity.Transactions, error) { FROM pfbudget.transactions t LEFT JOIN pfbudget.transactions_categorized tc ON t.id = tc.id + ORDER BY t.date DESC + LIMIT $1 + OFFSET $2 ` - rows, err := dal.Db.Query(stmt) + rows, err := dal.Db.Query(stmt, limit, offset) if err != nil { return entity.Transactions{}, err } diff --git a/internal/dal/impl_test.go b/internal/dal/impl_test.go index 400c692..9a4ec5a 100644 --- a/internal/dal/impl_test.go +++ b/internal/dal/impl_test.go @@ -142,18 +142,23 @@ func TestDalImpl_Transactions(t *testing.T) { Db: tt.fields.Db, } + limit, offset := 0, 0 + mock. ExpectQuery(` ^SELECT \w+\.id, \w+\.date, \w+\.description, \w+\.amount, \w+\.name FROM \w+\.transactions \w+ LEFT JOIN \w+\.transactions_categorized \w+ - ON \w+\.id = \w+\.id`). - WithoutArgs(). + ON \w+\.id = \w+\.id + ORDER BY \w+\.date DESC + LIMIT \$1 + OFFSET \$2`). + WithArgs(limit, offset). WillReturnRows( mock.NewRows([]string{"id", "date", "description", "amount", "category"}).AddRows(tt.mocks...), ) - got, err := dal.Transactions() + got, err := dal.Transactions(limit, offset) if (err != nil) != tt.wantErr { t.Errorf("DalImpl.Transactions() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/internal/mock/mock_dal.gen.go b/internal/mock/mock_dal.gen.go index 96c6c40..c43f06b 100644 --- a/internal/mock/mock_dal.gen.go +++ b/internal/mock/mock_dal.gen.go @@ -20,6 +20,7 @@ import ( type MockDAL struct { ctrl *gomock.Controller recorder *MockDALMockRecorder + isgomock struct{} } // MockDALMockRecorder is the mock recorder for MockDAL. @@ -40,18 +41,18 @@ func (m *MockDAL) EXPECT() *MockDALMockRecorder { } // Bank mocks base method. -func (m *MockDAL) Bank(arg0 string) (*entity.Bank, error) { +func (m *MockDAL) Bank(bankId string) (*entity.Bank, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Bank", arg0) + ret := m.ctrl.Call(m, "Bank", bankId) ret0, _ := ret[0].(*entity.Bank) ret1, _ := ret[1].(error) return ret0, ret1 } // Bank indicates an expected call of Bank. -func (mr *MockDALMockRecorder) Bank(arg0 any) *gomock.Call { +func (mr *MockDALMockRecorder) Bank(bankId any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Bank", reflect.TypeOf((*MockDAL)(nil).Bank), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Bank", reflect.TypeOf((*MockDAL)(nil).Bank), bankId) } // Banks mocks base method. @@ -100,18 +101,18 @@ func (mr *MockDALMockRecorder) InsertTransaction(arg0 any) *gomock.Call { } // Transaction mocks base method. -func (m *MockDAL) Transaction(arg0 int64) (*entity.Transaction, error) { +func (m *MockDAL) Transaction(transactionId int64) (*entity.Transaction, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Transaction", arg0) + ret := m.ctrl.Call(m, "Transaction", transactionId) ret0, _ := ret[0].(*entity.Transaction) ret1, _ := ret[1].(error) return ret0, ret1 } // Transaction indicates an expected call of Transaction. -func (mr *MockDALMockRecorder) Transaction(arg0 any) *gomock.Call { +func (mr *MockDALMockRecorder) Transaction(transactionId any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Transaction", reflect.TypeOf((*MockDAL)(nil).Transaction), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Transaction", reflect.TypeOf((*MockDAL)(nil).Transaction), transactionId) } // TransactionExists mocks base method. @@ -130,18 +131,18 @@ func (mr *MockDALMockRecorder) TransactionExists(arg0 any) *gomock.Call { } // Transactions mocks base method. -func (m *MockDAL) Transactions() ([]entity.Transaction, error) { +func (m *MockDAL) Transactions(limit, offset int) ([]entity.Transaction, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Transactions") + ret := m.ctrl.Call(m, "Transactions", limit, offset) ret0, _ := ret[0].([]entity.Transaction) ret1, _ := ret[1].(error) return ret0, ret1 } // Transactions indicates an expected call of Transactions. -func (mr *MockDALMockRecorder) Transactions() *gomock.Call { +func (mr *MockDALMockRecorder) Transactions(limit, offset any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Transactions", reflect.TypeOf((*MockDAL)(nil).Transactions)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Transactions", reflect.TypeOf((*MockDAL)(nil).Transactions), limit, offset) } // UpdateTransaction mocks base method.