Support modifying a transaction category
through the PUT /transactions/{transactionId} method.
The category is also returned on a /transaction(s) call.
Restrict the PUT to changing only the category. The other existing
attributes should remain immutable.
Remove the body of the PUT response, it isn't required, and it was
returning a 204, which shouldn't have it.
This patch also extracts the CategoryName as a separate component on the
OpenAPI spec, so that it can be reused on the Transaction.
Issues #26 and #23
This commit is contained in:
parent
9e58641001
commit
ca692e9aaf
@ -20,7 +20,15 @@ func (dal *DalImpl) Transaction(transactionId int64) (*entity.Transaction, error
|
|||||||
log.Panic("database not available")
|
log.Panic("database not available")
|
||||||
}
|
}
|
||||||
|
|
||||||
rows, err := dal.Db.Query("SELECT t.id, t.date, t.description, t.amount FROM pfbudget.transactions t WHERE t.id = $1", transactionId)
|
stmt := `
|
||||||
|
SELECT t.id, t.date, t.description, t.amount, tc.name
|
||||||
|
FROM pfbudget.transactions t
|
||||||
|
LEFT JOIN pfbudget.transactions_categorized tc
|
||||||
|
ON t.id = tc.id
|
||||||
|
WHERE t.id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
rows, err := dal.Db.Query(stmt, transactionId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -40,7 +48,14 @@ func (dal *DalImpl) Transactions() (entity.Transactions, error) {
|
|||||||
log.Panic("database not available")
|
log.Panic("database not available")
|
||||||
}
|
}
|
||||||
|
|
||||||
rows, err := dal.Db.Query("SELECT t.id, t.date, t.description, t.amount FROM pfbudget.transactions t")
|
stmt := `
|
||||||
|
SELECT t.id, t.date, t.description, t.amount, tc.name
|
||||||
|
FROM pfbudget.transactions t
|
||||||
|
LEFT JOIN pfbudget.transactions_categorized tc
|
||||||
|
ON t.id = tc.id
|
||||||
|
`
|
||||||
|
|
||||||
|
rows, err := dal.Db.Query(stmt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return entity.Transactions{}, err
|
return entity.Transactions{}, err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,27 +26,38 @@ func TestDalImpl_Transaction(t *testing.T) {
|
|||||||
}
|
}
|
||||||
type args struct {
|
type args struct {
|
||||||
transactionId int64
|
transactionId int64
|
||||||
rows [][]driver.Value
|
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
fields fields
|
fields fields
|
||||||
args args
|
args args
|
||||||
|
mocks [][]driver.Value
|
||||||
want *entity.Transaction
|
want *entity.Transaction
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"200",
|
"SelectTransaction",
|
||||||
fields{db},
|
fields{db},
|
||||||
args{
|
args{1},
|
||||||
1,
|
[][]driver.Value{
|
||||||
[][]driver.Value{
|
{1, date, "income", 1000, nil},
|
||||||
{1, date, "income", 1000},
|
},
|
||||||
}},
|
|
||||||
&entity.Transaction{Id: 1, Date: date, Description: "income", Value: decimal.NewFromInt(1000)},
|
&entity.Transaction{Id: 1, Date: date, Description: "income", Value: decimal.NewFromInt(1000)},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
{"404", fields{db}, args{2, nil}, nil, false},
|
{
|
||||||
|
"SelectTransactionWithCategory",
|
||||||
|
fields{db},
|
||||||
|
args{1},
|
||||||
|
[][]driver.Value{
|
||||||
|
{1, date, "income", 1000, "C1"},
|
||||||
|
},
|
||||||
|
&entity.Transaction{Id: 1, Date: date, Description: "income", Value: decimal.NewFromInt(1000), Category: golang.Ptr("C1")},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"SelectNoTransaction", fields{db}, args{2}, nil, nil, false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
@ -55,12 +66,15 @@ func TestDalImpl_Transaction(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mock.
|
mock.
|
||||||
ExpectQuery("^SELECT .* FROM .*transactions t WHERE t.id = \\$1$").
|
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
|
||||||
|
WHERE \w+\.id = \$1$`).
|
||||||
WithArgs(tt.args.transactionId).
|
WithArgs(tt.args.transactionId).
|
||||||
WillReturnRows(
|
WillReturnRows(
|
||||||
mock.
|
mock.NewRows([]string{"id", "date", "description", "amount", "category"}).AddRows(tt.mocks...),
|
||||||
NewRows([]string{"id", "date", "description", "amount"}).
|
|
||||||
AddRows(tt.args.rows...),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
got, err := d.Transaction(tt.args.transactionId)
|
got, err := d.Transaction(tt.args.transactionId)
|
||||||
@ -85,30 +99,42 @@ func TestDalImpl_Transactions(t *testing.T) {
|
|||||||
type fields struct {
|
type fields struct {
|
||||||
Db *sql.DB
|
Db *sql.DB
|
||||||
}
|
}
|
||||||
type args struct {
|
|
||||||
rows [][]driver.Value
|
|
||||||
}
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
fields fields
|
fields fields
|
||||||
args args
|
mocks [][]driver.Value
|
||||||
want entity.Transactions
|
want entity.Transactions
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"200",
|
"SelectTransactions",
|
||||||
fields{db},
|
fields{db},
|
||||||
args{[][]driver.Value{
|
[][]driver.Value{
|
||||||
{1, date, "income", 1000},
|
{1, date, "income", 1000, nil},
|
||||||
{2, date, "expense", -10.50},
|
{2, date, "expense", -10.50, nil},
|
||||||
}},
|
},
|
||||||
entity.Transactions{
|
entity.Transactions{
|
||||||
{Id: 1, Date: date, Description: "income", Value: decimal.NewFromInt(1000)},
|
{Id: 1, Date: date, Description: "income", Value: decimal.NewFromInt(1000)},
|
||||||
{Id: 2, Date: date, Description: "expense", Value: decimal.NewFromFloat(-10.50)},
|
{Id: 2, Date: date, Description: "expense", Value: decimal.NewFromFloat(-10.50)},
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
{"204", fields{db}, args{}, nil, false},
|
{
|
||||||
|
"SelectTransactionsWithCategory",
|
||||||
|
fields{db},
|
||||||
|
[][]driver.Value{
|
||||||
|
{1, date, "income", 1000, "C1"},
|
||||||
|
{2, date, "expense", -10.50, nil},
|
||||||
|
},
|
||||||
|
entity.Transactions{
|
||||||
|
{Id: 1, Date: date, Description: "income", Value: decimal.NewFromInt(1000), Category: golang.Ptr("C1")},
|
||||||
|
{Id: 2, Date: date, Description: "expense", Value: decimal.NewFromFloat(-10.50)},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"SelectNoTransactions", fields{db}, nil, nil, false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
@ -117,12 +143,14 @@ func TestDalImpl_Transactions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mock.
|
mock.
|
||||||
ExpectQuery("^SELECT .* FROM .*transactions t$").
|
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().
|
WithoutArgs().
|
||||||
WillReturnRows(
|
WillReturnRows(
|
||||||
mock.
|
mock.NewRows([]string{"id", "date", "description", "amount", "category"}).AddRows(tt.mocks...),
|
||||||
NewRows([]string{"id", "date", "description", "amount"}).
|
|
||||||
AddRows(tt.args.rows...),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
got, err := dal.Transactions()
|
got, err := dal.Transactions()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user