Compare commits

...

2 Commits

Author SHA1 Message Date
4c82ca0e2b
Implements /transactions POST method
It adds the method to the OpenAPI spec and generates a new server
config. The requirement for the ID on the Transaction component is
removed, so that it can be reused for insertions.
It also adds two new middlewares, a logging and a spec validator. If a
request does not follow the spec, a 400 is returned immediately.

Issue: #18
2024-06-22 20:11:52 +01:00
59f4546a81
Adds missing OpenAPI configuration file
Also locks the tools versions and launches them from the packages
installed by the go mod. Otherwise, they would have to be manually
installed beforehand.
2024-05-28 21:54:48 +01:00
15 changed files with 286 additions and 26 deletions

View File

@ -12,6 +12,8 @@ import (
_ "github.com/jackc/pgx/v5/stdlib"
"github.com/joho/godotenv"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
echomiddleware "github.com/oapi-codegen/echo-middleware"
)
func main() {
@ -41,6 +43,14 @@ func main() {
// 3. HTTP server
e := echo.New()
e.Use(middleware.Logger())
swagger, err := api.GetSwagger()
if err != nil {
log.Fatalf("unable to get API swagger: %v\n", err)
}
e.Use(echomiddleware.OapiRequestValidator(swagger))
handlers := api.ServerImpl{Dal: &dal}
api.RegisterHandlers(e, &handlers)

View File

@ -56,6 +56,23 @@ paths:
$ref: "#/components/schemas/Transactions"
"204":
description: No transactions
post:
summary: Create a new transaction
operationId: createTransaction
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/Transaction"
responses:
"201":
description: Transaction created
content:
application/json:
schema:
$ref: "#/components/schemas/Transaction"
"400":
description: Transaction not created
/transaction/{transactionId}:
get:
@ -138,7 +155,6 @@ components:
category:
type: string
required:
- id
- date
- description
- value

8
go.mod
View File

@ -7,6 +7,9 @@ require (
github.com/deepmap/oapi-codegen/v2 v2.1.0
github.com/google/uuid v1.6.0
github.com/jackc/pgx/v5 v5.5.5
github.com/joho/godotenv v1.5.1
github.com/labstack/echo/v4 v4.12.0
github.com/oapi-codegen/echo-middleware v1.0.1
github.com/oapi-codegen/runtime v1.1.1
github.com/shopspring/decimal v1.4.0
github.com/stretchr/testify v1.9.0
@ -19,13 +22,13 @@ require (
github.com/getkin/kin-openapi v0.124.0
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/invopop/yaml v0.3.1 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect
github.com/jackc/puddle/v2 v2.2.1 // indirect
github.com/joho/godotenv v1.5.1
github.com/josharian/intern v1.0.0 // indirect
github.com/labstack/echo/v4 v4.12.0
github.com/labstack/gommon v0.4.2 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
@ -41,6 +44,7 @@ require (
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/text v0.15.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.21.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect

8
go.sum
View File

@ -17,8 +17,12 @@ github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+Gr
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/invopop/yaml v0.3.1 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso=
github.com/invopop/yaml v0.3.1/go.mod h1:PMOp3nn4/12yEZUFfmOuNHJsZToEEOwoWsT+D81KkeA=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
@ -52,6 +56,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/oapi-codegen/echo-middleware v1.0.1 h1:edYGScq1phCcuDoz9AqA9eHX+tEI1LNL5PL1lkkQh1k=
github.com/oapi-codegen/echo-middleware v1.0.1/go.mod h1:DBQKRn+D/vfXOFbaX5GRwFttoJY64JH6yu+pdt7wU3o=
github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro=
github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg=
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
@ -92,6 +98,8 @@ golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw=
golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@ -0,0 +1,6 @@
package: api
generate:
echo-server: true
models: true
embedded-spec: true
output: server.gen.go

View File

@ -3,17 +3,41 @@ package api
import (
"git.rosemyrtle.work/personal-finance/server/internal/entity"
openapi_types "github.com/oapi-codegen/runtime/types"
"github.com/shopspring/decimal"
)
func convertTransaction(t entity.Transaction) Transaction {
func ptr[T any](v T) *T {
return &v
}
func entity2transaction(t entity.Transaction) Transaction {
return Transaction{
nil, openapi_types.Date{Time: t.Date}, t.Description, int64(t.Id), float32(t.Value.InexactFloat64())}
nil,
openapi_types.Date{Time: t.Date},
t.Description,
ptr(int64(t.Id)),
float32(t.Value.InexactFloat64()),
}
}
func transaction2entity(t Transaction) entity.Transaction {
var id uint64 = entity.InvalidId
if t.Id != nil {
id = uint64(*t.Id)
}
return entity.Transaction{
Id: id,
Date: t.Date.Time,
Description: t.Description,
Value: decimal.NewFromFloat32(t.Value),
}
}
func convertTransactions(ts entity.Transactions) Transactions {
var ans Transactions
for _, t := range ts {
ans = append(ans, convertTransaction(t))
ans = append(ans, entity2transaction(t))
}
return ans
}

View File

@ -8,7 +8,7 @@ import (
"github.com/labstack/echo/v4"
)
//go:generate oapi-codegen --config=api.cfg.yaml ../../docs/openapi.yaml
//go:generate go run github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen@v2.1.0 --config=api.cfg.yaml ../../docs/openapi.yaml
type ServerImpl struct {
Dal dal.DAL
@ -73,5 +73,21 @@ func (server *ServerImpl) GetTransactionById(ctx echo.Context, transactionId int
return ctx.NoContent(http.StatusNotFound)
}
return ctx.JSON(http.StatusOK, convertTransaction(*transaction))
return ctx.JSON(http.StatusOK, entity2transaction(*transaction))
}
func (server *ServerImpl) CreateTransaction(ctx echo.Context) error {
t := new(Transaction)
if err := ctx.Bind(t); err != nil {
log.Printf("%v", err)
return ctx.NoContent(http.StatusBadRequest)
}
ans, err := server.Dal.InsertTransaction(transaction2entity(*t))
if err != nil {
log.Printf("%v", err)
return ctx.NoContent(http.StatusInternalServerError)
}
return ctx.JSON(http.StatusCreated, entity2transaction(ans))
}

View File

@ -3,7 +3,9 @@ package api
import (
"net/http"
"net/http/httptest"
"reflect"
"strconv"
"strings"
"testing"
"time"
@ -224,3 +226,63 @@ func TestServerImpl_GetTransactionById(t *testing.T) {
})
}
}
func TestServerImpl_CreateTransaction(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mock_dal := mock.NewMockDAL(ctrl)
date, _ := time.Parse(time.DateOnly, "1974-04-25")
type fields struct {
Dal dal.DAL
}
type args struct {
request string
}
type want struct {
entity entity.Transaction
response string
}
tests := []struct {
name string
fields fields
args args
want want
wantErr bool
}{
{
"201",
fields{mock_dal},
args{`{"date": "1974-04-25", "description": "freedom", "value": 9000}`},
want{
entity.Transaction{Id: 1, Date: date, Description: "freedom", Value: decimal.New(9000, 0)},
`{"date":"1974-04-25","description":"freedom","id":1,"value":9000}`,
},
false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := &ServerImpl{Dal: tt.fields.Dal}
req := httptest.NewRequest(http.MethodPost, "/transactions", strings.NewReader(tt.args.request))
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
rec := httptest.NewRecorder()
e := echo.New()
ctx := e.NewContext(req, rec)
inserted := tt.want.entity
inserted.Id = entity.InvalidId
mock_dal.EXPECT().InsertTransaction(gomock.AssignableToTypeOf(reflect.TypeOf(tt.want.entity))).Return(tt.want.entity, nil).Times(1)
if err := s.CreateTransaction(ctx); (err != nil) != tt.wantErr {
t.Errorf("ServerImpl.GetTransactionById() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(strings.TrimSpace(rec.Body.String()), tt.want.response) {
t.Errorf("DalImpl.InsertTransaction() = %v, want %v", rec.Body.String(), tt.want.response)
}
})
}
}

View File

@ -34,7 +34,7 @@ type Transaction struct {
Category *string `json:"category,omitempty"`
Date openapi_types.Date `json:"date"`
Description string `json:"description"`
Id int64 `json:"id"`
Id *int64 `json:"id,omitempty"`
Value float32 `json:"value"`
}
@ -59,6 +59,9 @@ type GetTransactionsParams struct {
Sort *string `form:"sort,omitempty" json:"sort,omitempty"`
}
// CreateTransactionJSONRequestBody defines body for CreateTransaction for application/json ContentType.
type CreateTransactionJSONRequestBody = Transaction
// ServerInterface represents all server handlers.
type ServerInterface interface {
// Find bank by ID
@ -73,6 +76,9 @@ type ServerInterface interface {
// Retrieve existing transactions
// (GET /transactions)
GetTransactions(ctx echo.Context, params GetTransactionsParams) error
// Create a new transaction
// (POST /transactions)
CreateTransaction(ctx echo.Context) error
}
// ServerInterfaceWrapper converts echo contexts to parameters.
@ -167,6 +173,15 @@ func (w *ServerInterfaceWrapper) GetTransactions(ctx echo.Context) error {
return err
}
// CreateTransaction converts echo context to params.
func (w *ServerInterfaceWrapper) CreateTransaction(ctx echo.Context) error {
var err error
// Invoke the callback with all the unmarshaled arguments
err = w.Handler.CreateTransaction(ctx)
return err
}
// This is a simple interface which specifies echo.Route addition functions which
// are present on both echo.Echo and echo.Group, since we want to allow using
// either of them for path registration
@ -199,27 +214,29 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
router.GET(baseURL+"/banks", wrapper.GetBanks)
router.GET(baseURL+"/transaction/:transactionId", wrapper.GetTransactionById)
router.GET(baseURL+"/transactions", wrapper.GetTransactions)
router.POST(baseURL+"/transactions", wrapper.CreateTransaction)
}
// Base64 encoded, gzipped, json marshaled Swagger object
var swaggerSpec = []string{
"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",
"jkMmmIZdTHto9N8AAAD//+YRCdlGDQAA",
}
// GetSwagger returns the content of the embedded swagger specification file

View File

@ -5,8 +5,9 @@ import "git.rosemyrtle.work/personal-finance/server/internal/entity"
type DAL interface {
Transaction(transactionId int64) (*entity.Transaction, error)
Transactions() (entity.Transactions, error)
InsertTransaction(entity.Transaction) (entity.Transaction, error)
Bank(bankId string) (*entity.Bank, error)
Banks() (entity.Banks, error)
}
//go:generate mockgen -destination=../mock/mock_dal.gen.go -package=mock . DAL
//go:generate go run go.uber.org/mock/mockgen@latest -destination=../mock/mock_dal.gen.go -package=mock . DAL

View File

@ -46,6 +46,28 @@ func (dal *DalImpl) Transactions() (entity.Transactions, error) {
return convert[entity.Transaction](rows), nil
}
func (dal *DalImpl) InsertTransaction(t entity.Transaction) (entity.Transaction, error) {
log.Print("DAL::InsertTransaction")
if dal.Db == nil {
log.Panic("database not available")
}
stmt := `
INSERT INTO pfbudget.transactions (date, description, amount)
VALUES ($1, $2, $3)
RETURNING id
`
id := new(uint64)
if err := dal.Db.QueryRow(stmt, t.Date, t.Description, t.Value).Scan(id); err != nil {
return entity.Transaction{}, err
}
t.Id = *id
return t, nil
}
func (dal *DalImpl) Bank(bankId string) (*entity.Bank, error) {
log.Printf("DAL::Bank(%v)", bankId)

View File

@ -260,3 +260,60 @@ func TestDalImpl_Banks(t *testing.T) {
})
}
}
func TestDalImpl_InsertTransaction(t *testing.T) {
db, mock, err := sqlmock.New()
if err != nil {
t.Fatal(err)
}
date := time.Now()
type fields struct {
Db *sql.DB
}
type args struct {
t entity.Transaction
}
tests := []struct {
name string
fields fields
args args
want entity.Transaction
wantErr bool
}{
{
"201",
fields{db},
args{entity.Transaction{Id: entity.InvalidId, Date: date, Description: "freedom", Value: decimal.NewFromInt(9000)}},
entity.Transaction{Id: 1, Date: date, Description: "freedom", Value: decimal.NewFromInt(9000)},
false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
dal := &DalImpl{
Db: tt.fields.Db,
}
mock.
ExpectQuery(`
INSERT INTO .* \(date, description, amount\)
VALUES \(\$1, \$2, \$3\)
RETURNING id`).
WithArgs(tt.args.t.Date, tt.args.t.Description, tt.args.t.Value).
WillReturnRows(
mock.NewRows([]string{"id"}).
AddRows([]driver.Value{tt.want.Id}),
)
got, err := dal.InsertTransaction(tt.args.t)
if (err != nil) != tt.wantErr {
t.Errorf("DalImpl.InsertTransaction() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("DalImpl.InsertTransaction() = %v, want %v", got, tt.want)
}
})
}
}

View File

@ -7,6 +7,8 @@ import (
"github.com/shopspring/decimal"
)
const InvalidId uint64 = 0
type Transaction struct {
Id uint64
Date time.Time

View File

@ -69,6 +69,21 @@ func (mr *MockDALMockRecorder) Banks() *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Banks", reflect.TypeOf((*MockDAL)(nil).Banks))
}
// InsertTransaction mocks base method.
func (m *MockDAL) InsertTransaction(arg0 entity.Transaction) (entity.Transaction, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "InsertTransaction", arg0)
ret0, _ := ret[0].(entity.Transaction)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// InsertTransaction indicates an expected call of InsertTransaction.
func (mr *MockDALMockRecorder) InsertTransaction(arg0 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertTransaction", reflect.TypeOf((*MockDAL)(nil).InsertTransaction), arg0)
}
// Transaction mocks base method.
func (m *MockDAL) Transaction(arg0 int64) (*entity.Transaction, error) {
m.ctrl.T.Helper()

View File

@ -1,7 +1,7 @@
//go:build tools
// +build tools
package main
package tools
import (
_ "github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen"