This commit is contained in:
Luís Murta 2024-03-09 11:04:07 +00:00
parent dcadfe73e8
commit 3d9de744e7
Signed by: satprog
GPG Key ID: 169EF1BBD7049F94
4 changed files with 113 additions and 3 deletions

1
go.mod
View File

@ -3,6 +3,7 @@ module git.rosemyrtle.work/personal-finance/server
go 1.21.1
require (
github.com/DATA-DOG/go-sqlmock v1.5.2
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
github.com/deepmap/oapi-codegen/v2 v2.1.0 // indirect
github.com/getkin/kin-openapi v0.123.0 // indirect

3
go.sum
View File

@ -1,3 +1,5 @@
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
@ -18,6 +20,7 @@ github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdi
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
github.com/labstack/echo/v4 v4.11.4 h1:vDZmA+qNeh1pd/cCkEicDMrjtrnMGQ1QFI9gWN1zGq8=
github.com/labstack/echo/v4 v4.11.4/go.mod h1:noh7EvLwqDsmh/X/HWKPUl1AjzJrhyptRyEbQJfxen8=
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=

View File

@ -2,12 +2,19 @@ package api
//go:generate go run github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen --config=api.cfg.yaml ../../docs/openapi.yaml
import (
"database/sql"
"errors"
"log"
"net/http"
"reflect"
"github.com/labstack/echo/v4"
openapi_types "github.com/oapi-codegen/runtime/types"
)
type PersonalFinanceImpl struct{}
type PersonalFinanceImpl struct {
db *sql.DB
}
func (*PersonalFinanceImpl) GetBanks(ctx echo.Context) error {
return echo.NewHTTPError(http.StatusNotImplemented)
@ -17,10 +24,48 @@ func (*PersonalFinanceImpl) GetBanksById(ctx echo.Context, bankId int64) error {
return echo.NewHTTPError(http.StatusNotImplemented)
}
func (*PersonalFinanceImpl) GetTransactions(ctx echo.Context, params GetTransactionsParams) error {
return echo.NewHTTPError(http.StatusNotImplemented)
func (pf *PersonalFinanceImpl) GetTransactions(ctx echo.Context, params GetTransactionsParams) error {
rows, err := pf.db.Query("SELECT t.id, t.date, t.description, t.amount, tc.category FROM pfbudget.transactions t LEFT JOIN pfbudget.transactions_categorized tc ON t.id = tc.id")
if err != nil {
log.Fatal(err)
}
return ctx.JSON(http.StatusOK, convert[Transaction](rows))
}
func (*PersonalFinanceImpl) GetTransactionsById(ctx echo.Context, transactionId int64) error {
return echo.NewHTTPError(http.StatusNotImplemented)
}
func convert[T any](rows *sql.Rows) []T {
var ans []T
for rows.Next() {
var r T
s := reflect.ValueOf(&r).Elem()
log.Println(s)
numCols := s.NumField()
columns := make([]interface{}, numCols)
log.Println(columns)
for i := 0; i < numCols; i++ {
field := s.Field(i)
columns[i] = field.Addr().Interface()
}
if err := rows.Scan(columns...); err != nil {
log.Fatal(err)
}
ans = append(ans, r)
}
return ans
}
func (d *openapi_types.Date) Scan(value interface{}) error {
if value == nil {
return errors.New("Null date")
}
// if bv, err := driver.
}

61
internal/api/impl_test.go Normal file
View File

@ -0,0 +1,61 @@
package api
import (
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/DATA-DOG/go-sqlmock"
"github.com/labstack/echo/v4"
)
func TestGetTransactions(t *testing.T) {
db, mock, err := sqlmock.New()
if err != nil {
t.Fatal(err)
}
e := echo.New()
handlers := PersonalFinanceImpl{db}
RegisterHandlers(e, &handlers)
t.Run("when successful", func(t *testing.T) {
rec := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/transactions", nil)
date := time.Now()
rows := mock.NewRows([]string{"id", "date", "description", "amount", "category"}).
AddRow(1, date, "#1", 1000, nil).
AddRow(2, date, "#2", -1000, "expense")
mock.ExpectQuery("SELECT t.id, t.date, t.description, t.amount, tc.category FROM pfbudget.transactions t LEFT JOIN pfbudget.transactions_categorized tc ON t.id = tc.id").WillReturnRows(rows)
ctx := e.NewContext(req, rec)
err := handlers.GetTransactions(ctx, GetTransactionsParams{})
if err != nil {
t.Error(err)
}
if rec.Code != http.StatusOK {
t.Error(rec.Code)
}
expected := `[
{
"id":1,
"date":"` + date.Format(time.DateOnly) + `",
description": "#1",
"amount":1000
},
{
"id":2,
"date":"` + date.Format(time.DateOnly) + `",
description": "#2",
"amount":-1000,
"category": "expense"
},
]`
if ret := rec.Body.String(); ret != expected {
t.Error(ret)
}
})
}