Compare commits
No commits in common. "b30a5c5c2d0c0bbbb3ecef36a13ff9a0941099da" and "0258ff6620c9984011f55300b3b186a9a7e4212d" have entirely different histories.
b30a5c5c2d
...
0258ff6620
13
README.md
13
README.md
@ -564,16 +564,3 @@ querier.
|
|||||||
Since this repo layer is just a wrapping layer between the `sqlc.models` and
|
Since this repo layer is just a wrapping layer between the `sqlc.models` and
|
||||||
my own models, I can extract the conversion part to functions and test them.
|
my own models, I can extract the conversion part to functions and test them.
|
||||||
I'm not testing the whole thing but I test what I can.
|
I'm not testing the whole thing but I test what I can.
|
||||||
|
|
||||||
### 2024/10/24
|
|
||||||
|
|
||||||
When writing the tests. I am asking myself the differences between `[]T`,
|
|
||||||
`[]*T` and `*[]T`.
|
|
||||||
|
|
||||||
`*[]T` is simple, it is a reference to the original slice. So modifying it
|
|
||||||
means modifying the original slice.
|
|
||||||
|
|
||||||
But between `[]*T` and `[]T`, the only difference that I see (pointed out by
|
|
||||||
`ChatGPT`) is how the memory is allocated. With `[]T` it might be better for
|
|
||||||
the GC to deal with the memory free. I thing for my project I will stick to
|
|
||||||
`[]T`.
|
|
||||||
|
@ -35,7 +35,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type eventRepository struct {
|
type eventRepository struct {
|
||||||
queries *sqlc.Queries
|
queries sqlc.Querier
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEventRepository(db *sql.DB) repo.EventRepository {
|
func NewEventRepository(db *sql.DB) repo.EventRepository {
|
||||||
@ -62,7 +62,7 @@ func convToEventRetrieved(eventDTO *sqlc.GetEventByIDRow) (*model.EventRetrieved
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var users []model.UserBaseRetrieved
|
var users []*model.UserBaseRetrieved
|
||||||
err = json.Unmarshal(eventDTO.Users, &users)
|
err = json.Unmarshal(eventDTO.Users, &users)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Unexpected
|
// Unexpected
|
||||||
@ -99,8 +99,8 @@ func (e *eventRepository) GetByID(ctx context.Context, eventID int) (*model.Even
|
|||||||
return convToEventRetrieved(&eventDTO)
|
return convToEventRetrieved(&eventDTO)
|
||||||
}
|
}
|
||||||
|
|
||||||
func convToEventList(eventsDTO []sqlc.ListEventsByUserIDRow) ([]model.EventListRetrieved, error) {
|
func convToEventList(eventsDTO []sqlc.ListEventsByUserIDRow) ([]*model.EventListRetrieved, error) {
|
||||||
var events []model.EventListRetrieved
|
var events []*model.EventListRetrieved
|
||||||
|
|
||||||
for _, evDTO := range eventsDTO {
|
for _, evDTO := range eventsDTO {
|
||||||
var owner model.UserBaseRetrieved
|
var owner model.UserBaseRetrieved
|
||||||
@ -110,12 +110,10 @@ func convToEventList(eventsDTO []sqlc.ListEventsByUserIDRow) ([]model.EventListR
|
|||||||
log.ErrorLog("json unmarshal error", "err", err)
|
log.ErrorLog("json unmarshal error", "err", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
ev := model.EventListRetrieved{
|
ev := &model.EventListRetrieved{
|
||||||
ID: int(evDTO.ID),
|
ID: int(evDTO.ID),
|
||||||
Name: evDTO.Name,
|
Name: evDTO.Name,
|
||||||
Description: evDTO.Description.String,
|
Description: evDTO.Description.String,
|
||||||
Owner: &owner,
|
|
||||||
CreatedAt: evDTO.CreatedAt,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
events = append(events, ev)
|
events = append(events, ev)
|
||||||
@ -128,7 +126,7 @@ func convToEventList(eventsDTO []sqlc.ListEventsByUserIDRow) ([]model.EventListR
|
|||||||
func (e *eventRepository) ListEventsByUserID(
|
func (e *eventRepository) ListEventsByUserID(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
userID int,
|
userID int,
|
||||||
) ([]model.EventListRetrieved, error) {
|
) ([]*model.EventListRetrieved, error) {
|
||||||
eventsDTO, err := e.queries.ListEventsByUserID(ctx, int32(userID))
|
eventsDTO, err := e.queries.ListEventsByUserID(ctx, int32(userID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorLog("query error", "err", err)
|
log.ErrorLog("query error", "err", err)
|
||||||
|
@ -1,122 +0,0 @@
|
|||||||
// MIT License
|
|
||||||
//
|
|
||||||
// Copyright (c) 2024 vinchent <vinchent@vinchent.xyz>
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all
|
|
||||||
// copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
// SOFTWARE.
|
|
||||||
|
|
||||||
package repo
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"encoding/json"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/adapter/repo/sqlc"
|
|
||||||
"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/model"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestConvToEventRetrieved(t *testing.T) {
|
|
||||||
input := &sqlc.GetEventByIDRow{
|
|
||||||
ID: 123,
|
|
||||||
Name: "event",
|
|
||||||
Description: sql.NullString{Valid: false},
|
|
||||||
TotalAmount: sql.NullInt32{Valid: false},
|
|
||||||
DefaultCurrency: "EUR",
|
|
||||||
CreatedAt: time.Date(2000, time.April, 11, 0, 0, 0, 0, time.UTC),
|
|
||||||
UpdatedAt: time.Date(2000, time.April, 11, 0, 0, 0, 0, time.UTC),
|
|
||||||
Owner: json.RawMessage(`{"id":1, "first_name":"owner", "last_name":"owner"}`),
|
|
||||||
Users: json.RawMessage(`[{"id":1, "first_name":"owner", "last_name":"owner"}]`),
|
|
||||||
}
|
|
||||||
|
|
||||||
want := &model.EventRetrieved{
|
|
||||||
ID: 123,
|
|
||||||
Name: "event",
|
|
||||||
Description: "",
|
|
||||||
TotalAmount: model.Money{Amount: 0, Currency: "EUR"},
|
|
||||||
DefaultCurrency: model.Currency("EUR"),
|
|
||||||
CreatedAt: time.Date(2000, time.April, 11, 0, 0, 0, 0, time.UTC),
|
|
||||||
UpdatedAt: time.Date(2000, time.April, 11, 0, 0, 0, 0, time.UTC),
|
|
||||||
Owner: &model.UserBaseRetrieved{
|
|
||||||
ID: 1,
|
|
||||||
FirstName: "owner",
|
|
||||||
LastName: "owner",
|
|
||||||
},
|
|
||||||
Users: []model.UserBaseRetrieved{
|
|
||||||
{
|
|
||||||
ID: 1,
|
|
||||||
FirstName: "owner",
|
|
||||||
LastName: "owner",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
got, err := convToEventRetrieved(input)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, want, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConvToEventList(t *testing.T) {
|
|
||||||
input := []sqlc.ListEventsByUserIDRow{
|
|
||||||
{
|
|
||||||
ID: 123,
|
|
||||||
Name: "event",
|
|
||||||
Description: sql.NullString{Valid: false},
|
|
||||||
CreatedAt: time.Date(2000, time.April, 11, 0, 0, 0, 0, time.UTC),
|
|
||||||
Owner: json.RawMessage(`{"id":1, "first_name":"owner", "last_name":"owner"}`),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ID: 456,
|
|
||||||
Name: "event2",
|
|
||||||
Description: sql.NullString{String: "super event", Valid: true},
|
|
||||||
CreatedAt: time.Date(2000, time.April, 11, 0, 0, 0, 0, time.UTC),
|
|
||||||
Owner: json.RawMessage(`{"id":1, "first_name":"owner", "last_name":"owner"}`),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
want := []model.EventListRetrieved{
|
|
||||||
{
|
|
||||||
ID: 123,
|
|
||||||
Name: "event",
|
|
||||||
Description: "",
|
|
||||||
CreatedAt: time.Date(2000, time.April, 11, 0, 0, 0, 0, time.UTC),
|
|
||||||
Owner: &model.UserBaseRetrieved{
|
|
||||||
ID: 1,
|
|
||||||
FirstName: "owner",
|
|
||||||
LastName: "owner",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ID: 456,
|
|
||||||
Name: "event2",
|
|
||||||
Description: "super event",
|
|
||||||
CreatedAt: time.Date(2000, time.April, 11, 0, 0, 0, 0, time.UTC),
|
|
||||||
Owner: &model.UserBaseRetrieved{
|
|
||||||
ID: 1,
|
|
||||||
FirstName: "owner",
|
|
||||||
LastName: "owner",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
got, err := convToEventList(input)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, want, got)
|
|
||||||
}
|
|
@ -1,231 +0,0 @@
|
|||||||
// MIT License
|
|
||||||
//
|
|
||||||
// Copyright (c) 2024 vinchent <vinchent@vinchent.xyz>
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all
|
|
||||||
// copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
// SOFTWARE.
|
|
||||||
|
|
||||||
package repo
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"database/sql"
|
|
||||||
"encoding/json"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/adapter/repo/sqlc"
|
|
||||||
"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/model"
|
|
||||||
"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/usecase/repo"
|
|
||||||
"git.vinchent.xyz/vinchent/howmuch/internal/pkg/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
type expenseRepository struct {
|
|
||||||
queries *sqlc.Queries
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewExpenseRepository(db *sql.DB) repo.ExpenseRepository {
|
|
||||||
return &expenseRepository{
|
|
||||||
queries: sqlc.New(db),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteExpense implements repo.ExpenseRepository.
|
|
||||||
func (e *expenseRepository) DeleteExpense(ctx context.Context, expenseID int) error {
|
|
||||||
return e.queries.DeleteExpense(ctx, int32(expenseID))
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteTransactionsOfExpense implements repo.ExpenseRepository.
|
|
||||||
func (e *expenseRepository) DeleteTransactionsOfExpense(ctx context.Context, expenseID int) error {
|
|
||||||
return e.queries.DeleteTransactionsOfExpenseID(ctx, int32(expenseID))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetExpenseByID implements repo.ExpenseRepository.
|
|
||||||
func (e *expenseRepository) GetExpenseByID(
|
|
||||||
ctx context.Context,
|
|
||||||
expenseID int,
|
|
||||||
) (*model.ExpenseRetrieved, error) {
|
|
||||||
expenseDTO, err := e.queries.GetExpenseByID(ctx, int32(expenseID))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
expense, err := convToExpenseRetrieved(&expenseDTO)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return expense, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func convToPayments(raw json.RawMessage) ([]model.Payment, error) {
|
|
||||||
var paymentsRetrieved []model.PaymentRetrieved
|
|
||||||
err := json.Unmarshal(raw, &paymentsRetrieved)
|
|
||||||
if err != nil {
|
|
||||||
// Unexpected
|
|
||||||
log.ErrorLog("json unmarshal error", "err", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var payments []model.Payment
|
|
||||||
for _, p := range paymentsRetrieved {
|
|
||||||
payment := model.Payment{
|
|
||||||
PayerID: p.PayerID,
|
|
||||||
PayerFirstName: p.PayerFirstName,
|
|
||||||
PayerLastName: p.PayerLastName,
|
|
||||||
Amount: model.MakeMoney(p.Amount, model.Currency(p.Currency)),
|
|
||||||
}
|
|
||||||
payments = append(payments, payment)
|
|
||||||
}
|
|
||||||
|
|
||||||
return payments, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func convToBenefits(raw json.RawMessage) ([]model.Benefit, error) {
|
|
||||||
var benefitsRetrieved []model.BenefitRetrieved
|
|
||||||
err := json.Unmarshal(raw, &benefitsRetrieved)
|
|
||||||
if err != nil {
|
|
||||||
// Unexpected
|
|
||||||
log.ErrorLog("json unmarshal error", "err", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var benefits []model.Benefit
|
|
||||||
for _, b := range benefitsRetrieved {
|
|
||||||
benefit := model.Benefit{
|
|
||||||
RecipientID: b.RecipientID,
|
|
||||||
RecipientFirstName: b.RecipientFirstName,
|
|
||||||
RecipientLastName: b.RecipientLastName,
|
|
||||||
Amount: model.MakeMoney(b.Amount, model.Currency(b.Currency)),
|
|
||||||
}
|
|
||||||
benefits = append(benefits, benefit)
|
|
||||||
}
|
|
||||||
|
|
||||||
return benefits, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func convToExpenseRetrieved(expenseDTO *sqlc.GetExpenseByIDRow) (*model.ExpenseRetrieved, error) {
|
|
||||||
payments, err := convToPayments(expenseDTO.Payments)
|
|
||||||
if err != nil {
|
|
||||||
// Unexpected
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
benefits, err := convToBenefits(expenseDTO.Benefits)
|
|
||||||
if err != nil {
|
|
||||||
// Unexpected
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
expenseRetrieved := &model.ExpenseRetrieved{
|
|
||||||
ID: int(expenseDTO.ID),
|
|
||||||
CreatedAt: expenseDTO.CreatedAt,
|
|
||||||
UpdatedAt: expenseDTO.UpdatedAt,
|
|
||||||
Amount: model.MakeMoney(int(expenseDTO.Amount), model.Currency(expenseDTO.Currency)),
|
|
||||||
EventID: int(expenseDTO.EventID),
|
|
||||||
Detail: model.ExpenseDetail{
|
|
||||||
Name: expenseDTO.Name.String,
|
|
||||||
Place: expenseDTO.Place.String,
|
|
||||||
},
|
|
||||||
Payments: payments,
|
|
||||||
Benefits: benefits,
|
|
||||||
}
|
|
||||||
|
|
||||||
return expenseRetrieved, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// InsertExpense implements repo.ExpenseRepository.
|
|
||||||
func (e *expenseRepository) InsertExpense(
|
|
||||||
ctx context.Context,
|
|
||||||
expenseEntity *model.ExpenseEntity,
|
|
||||||
) (*model.ExpenseEntity, error) {
|
|
||||||
expenseDTO, err := e.queries.InsertExpense(ctx, sqlc.InsertExpenseParams{
|
|
||||||
CreatedAt: time.Now(),
|
|
||||||
UpdatedAt: time.Now(),
|
|
||||||
Amount: int32(expenseEntity.Amount),
|
|
||||||
Currency: expenseEntity.Currency,
|
|
||||||
EventID: int32(expenseEntity.EventID),
|
|
||||||
Name: sql.NullString{String: expenseEntity.Name, Valid: true},
|
|
||||||
Place: sql.NullString{String: expenseEntity.Place, Valid: true},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &model.ExpenseEntity{
|
|
||||||
ID: int(expenseDTO.ID),
|
|
||||||
CreatedAt: expenseDTO.CreatedAt,
|
|
||||||
UpdatedAt: expenseDTO.CreatedAt,
|
|
||||||
Amount: int(expenseDTO.Amount),
|
|
||||||
Currency: expenseDTO.Currency,
|
|
||||||
EventID: int(expenseDTO.EventID),
|
|
||||||
Name: expenseDTO.Name.String,
|
|
||||||
Place: expenseDTO.Place.String,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListExpensesByEventID implements repo.ExpenseRepository.
|
|
||||||
func (e *expenseRepository) ListExpensesByEventID(
|
|
||||||
ctx context.Context,
|
|
||||||
id int,
|
|
||||||
) ([]model.ExpensesListRetrieved, error) {
|
|
||||||
listDTO, err := e.queries.ListExpensesByEventID(ctx, int32(id))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var res []model.ExpensesListRetrieved
|
|
||||||
for _, dto := range listDTO {
|
|
||||||
elem := model.ExpensesListRetrieved{
|
|
||||||
ID: int(dto.ID),
|
|
||||||
CreatedAt: dto.CreatedAt,
|
|
||||||
UpdatedAt: dto.UpdatedAt,
|
|
||||||
Amount: model.MakeMoney(int(dto.Amount), model.Currency(dto.Currency)),
|
|
||||||
EventID: int(dto.EventID),
|
|
||||||
Detail: model.ExpenseDetail{
|
|
||||||
Name: dto.Name.String,
|
|
||||||
Place: dto.Place.String,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
res = append(res, elem)
|
|
||||||
}
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateExpenseByID implements repo.ExpenseRepository.
|
|
||||||
func (e *expenseRepository) UpdateExpenseByID(
|
|
||||||
ctx context.Context,
|
|
||||||
expenseUpdate *model.ExpenseUpdateEntity,
|
|
||||||
) (*model.ExpenseEntity, error) {
|
|
||||||
expenseDTO, err := e.queries.UpdateExpenseByID(ctx, sqlc.UpdateExpenseByIDParams{
|
|
||||||
ID: int32(expenseUpdate.ID),
|
|
||||||
UpdatedAt: time.Now(),
|
|
||||||
Amount: int32(expenseUpdate.Amount),
|
|
||||||
Currency: expenseUpdate.Currency,
|
|
||||||
Name: sql.NullString{String: expenseUpdate.Name, Valid: true},
|
|
||||||
Place: sql.NullString{String: expenseUpdate.Place, Valid: true},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &model.ExpenseEntity{
|
|
||||||
ID: int(expenseDTO.ID),
|
|
||||||
CreatedAt: expenseDTO.CreatedAt,
|
|
||||||
UpdatedAt: expenseDTO.CreatedAt,
|
|
||||||
Amount: int(expenseDTO.Amount),
|
|
||||||
Currency: expenseDTO.Currency,
|
|
||||||
EventID: int(expenseDTO.EventID),
|
|
||||||
Name: expenseDTO.Name.String,
|
|
||||||
Place: expenseDTO.Place.String,
|
|
||||||
}, nil
|
|
||||||
}
|
|
@ -1,96 +0,0 @@
|
|||||||
// MIT License
|
|
||||||
//
|
|
||||||
// Copyright (c) 2024 vinchent <vinchent@vinchent.xyz>
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all
|
|
||||||
// copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
// SOFTWARE.
|
|
||||||
|
|
||||||
package repo
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"encoding/json"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/adapter/repo/sqlc"
|
|
||||||
"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/model"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestConvToExpenseRetrieved(t *testing.T) {
|
|
||||||
input := &sqlc.GetExpenseByIDRow{
|
|
||||||
ID: 123,
|
|
||||||
CreatedAt: time.Date(2000, time.April, 11, 0, 0, 0, 0, time.UTC),
|
|
||||||
UpdatedAt: time.Date(2000, time.April, 11, 0, 0, 0, 0, time.UTC),
|
|
||||||
Amount: 123,
|
|
||||||
Currency: "EUR",
|
|
||||||
EventID: 123,
|
|
||||||
Name: sql.NullString{Valid: false},
|
|
||||||
Place: sql.NullString{Valid: false},
|
|
||||||
Payments: json.RawMessage(
|
|
||||||
`[{"payer_id": 1, "payer_first_name": "toto", "payer_last_name": "titi", "amount": 10, "currency": "EUR"},
|
|
||||||
{"payer_id": 2, "payer_first_name": "tata", "payer_last_name": "titi", "amount": 10, "currency": "EUR"}]`,
|
|
||||||
),
|
|
||||||
Benefits: json.RawMessage(
|
|
||||||
`[{"recipient_id": 1, "recipient_first_name": "toto", "recipient_last_name": "titi", "amount": 10, "currency": "EUR"},
|
|
||||||
{"recipient_id": 2, "recipient_first_name": "tata", "recipient_last_name": "titi", "amount": 10, "currency": "EUR"}]`,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
want := &model.ExpenseRetrieved{
|
|
||||||
ID: 123,
|
|
||||||
CreatedAt: time.Date(2000, time.April, 11, 0, 0, 0, 0, time.UTC),
|
|
||||||
UpdatedAt: time.Date(2000, time.April, 11, 0, 0, 0, 0, time.UTC),
|
|
||||||
Amount: model.Money{Amount: 123, Currency: model.Currency("EUR")},
|
|
||||||
EventID: 123,
|
|
||||||
Detail: model.ExpenseDetail{},
|
|
||||||
Payments: []model.Payment{
|
|
||||||
{
|
|
||||||
PayerID: 1,
|
|
||||||
PayerFirstName: "toto",
|
|
||||||
PayerLastName: "titi",
|
|
||||||
Amount: model.Money{Amount: 10, Currency: model.Currency("EUR")},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PayerID: 2,
|
|
||||||
PayerFirstName: "tata",
|
|
||||||
PayerLastName: "titi",
|
|
||||||
Amount: model.Money{Amount: 10, Currency: model.Currency("EUR")},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Benefits: []model.Benefit{
|
|
||||||
{
|
|
||||||
RecipientID: 1,
|
|
||||||
RecipientFirstName: "toto",
|
|
||||||
RecipientLastName: "titi",
|
|
||||||
Amount: model.Money{Amount: 10, Currency: model.Currency("EUR")},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
RecipientID: 2,
|
|
||||||
RecipientFirstName: "tata",
|
|
||||||
RecipientLastName: "titi",
|
|
||||||
Amount: model.Money{Amount: 10, Currency: model.Currency("EUR")},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
got, err := convToExpenseRetrieved(input)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, want, got)
|
|
||||||
}
|
|
@ -24,6 +24,14 @@ FROM "expense" ex
|
|||||||
JOIN "event" ev ON ev.id = ex.event_id
|
JOIN "event" ev ON ev.id = ex.event_id
|
||||||
WHERE ev.id = $1;
|
WHERE ev.id = $1;
|
||||||
|
|
||||||
|
-- name: ListExpensesByEventIDByUserID :many
|
||||||
|
SELECT
|
||||||
|
ex.id, ex.created_at, ex.updated_at, ex.amount, ex.currency, ex.event_id,
|
||||||
|
ex.name, ex.place
|
||||||
|
FROM "expense" ex
|
||||||
|
JOIN "event" ev ON ev.id = ex.event_id
|
||||||
|
WHERE ev.id = $1;
|
||||||
|
|
||||||
-- name: GetExpenseByID :one
|
-- name: GetExpenseByID :one
|
||||||
WITH payer_transaction as (
|
WITH payer_transaction as (
|
||||||
SELECT pt.expense_id,
|
SELECT pt.expense_id,
|
||||||
|
@ -183,6 +183,47 @@ func (q *Queries) ListExpensesByEventID(ctx context.Context, id int32) ([]Expens
|
|||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const listExpensesByEventIDByUserID = `-- name: ListExpensesByEventIDByUserID :many
|
||||||
|
SELECT
|
||||||
|
ex.id, ex.created_at, ex.updated_at, ex.amount, ex.currency, ex.event_id,
|
||||||
|
ex.name, ex.place
|
||||||
|
FROM "expense" ex
|
||||||
|
JOIN "event" ev ON ev.id = ex.event_id
|
||||||
|
WHERE ev.id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) ListExpensesByEventIDByUserID(ctx context.Context, id int32) ([]Expense, error) {
|
||||||
|
rows, err := q.db.QueryContext(ctx, listExpensesByEventIDByUserID, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []Expense
|
||||||
|
for rows.Next() {
|
||||||
|
var i Expense
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
&i.Amount,
|
||||||
|
&i.Currency,
|
||||||
|
&i.EventID,
|
||||||
|
&i.Name,
|
||||||
|
&i.Place,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Close(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
const updateExpenseByID = `-- name: UpdateExpenseByID :one
|
const updateExpenseByID = `-- name: UpdateExpenseByID :one
|
||||||
UPDATE "expense"
|
UPDATE "expense"
|
||||||
SET updated_at = $2, amount = $3, currency = $4, name = $5, place = $6
|
SET updated_at = $2, amount = $3, currency = $4, name = $5, place = $6
|
||||||
|
@ -22,6 +22,7 @@ type Querier interface {
|
|||||||
InsertUser(ctx context.Context, arg InsertUserParams) (User, error)
|
InsertUser(ctx context.Context, arg InsertUserParams) (User, error)
|
||||||
ListEventsByUserID(ctx context.Context, userID int32) ([]ListEventsByUserIDRow, error)
|
ListEventsByUserID(ctx context.Context, userID int32) ([]ListEventsByUserIDRow, error)
|
||||||
ListExpensesByEventID(ctx context.Context, id int32) ([]Expense, error)
|
ListExpensesByEventID(ctx context.Context, id int32) ([]Expense, error)
|
||||||
|
ListExpensesByEventIDByUserID(ctx context.Context, id int32) ([]Expense, error)
|
||||||
UpdateEventByID(ctx context.Context, arg UpdateEventByIDParams) error
|
UpdateEventByID(ctx context.Context, arg UpdateEventByIDParams) error
|
||||||
UpdateExpenseByID(ctx context.Context, arg UpdateExpenseByIDParams) (Expense, error)
|
UpdateExpenseByID(ctx context.Context, arg UpdateExpenseByIDParams) (Expense, error)
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type userRepository struct {
|
type userRepository struct {
|
||||||
querier *sqlc.Queries
|
querier sqlc.Querier
|
||||||
}
|
}
|
||||||
|
|
||||||
const insertTimeout = 1 * time.Second
|
const insertTimeout = 1 * time.Second
|
||||||
|
@ -57,7 +57,7 @@ type EventInfoResponse struct {
|
|||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
UpdatedAt time.Time
|
UpdatedAt time.Time
|
||||||
|
|
||||||
Users []UserBaseResponse
|
Users []*UserBaseResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
@ -93,7 +93,7 @@ type EventRetrieved struct {
|
|||||||
Name string
|
Name string
|
||||||
Description string
|
Description string
|
||||||
|
|
||||||
Users []UserBaseRetrieved
|
Users []*UserBaseRetrieved
|
||||||
|
|
||||||
TotalAmount Money
|
TotalAmount Money
|
||||||
DefaultCurrency Currency
|
DefaultCurrency Currency
|
||||||
@ -121,9 +121,9 @@ type Event struct {
|
|||||||
Description string
|
Description string
|
||||||
|
|
||||||
// lazy get using participation join
|
// lazy get using participation join
|
||||||
Users []UserDO
|
Users []*UserDO
|
||||||
// lazy get
|
// lazy get
|
||||||
Expenses []Expense
|
Expenses []*Expense
|
||||||
|
|
||||||
TotalAmount Money
|
TotalAmount Money
|
||||||
DefaultCurrency Currency
|
DefaultCurrency Currency
|
||||||
|
@ -37,17 +37,7 @@ type ExpenseRequest struct {
|
|||||||
// }}}
|
// }}}
|
||||||
// {{{ Response
|
// {{{ Response
|
||||||
|
|
||||||
type (
|
type ExpensesListResponse struct {
|
||||||
ExpenseGetResponse Expense
|
|
||||||
ExpenseResponse ExpenseRetrieved
|
|
||||||
)
|
|
||||||
|
|
||||||
// }}}
|
|
||||||
// {{{ Retrieved
|
|
||||||
|
|
||||||
type ExpenseRetrieved Expense
|
|
||||||
|
|
||||||
type ExpensesListRetrieved struct {
|
|
||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
@ -58,21 +48,12 @@ type ExpensesListRetrieved struct {
|
|||||||
Detail ExpenseDetail `json:"detail"`
|
Detail ExpenseDetail `json:"detail"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PaymentRetrieved struct {
|
type ExpenseGetResponse Expense
|
||||||
PayerID int `json:"payer_id"`
|
|
||||||
PayerFirstName string `json:"payer_first_name"`
|
|
||||||
PayerLastName string `json:"payer_last_name"`
|
|
||||||
Amount int `json:"amount"`
|
|
||||||
Currency string `json:"currency"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type BenefitRetrieved struct {
|
// }}}
|
||||||
RecipientID int `json:"recipient_id"`
|
// {{{ Retrieved
|
||||||
RecipientFirstName string `json:"recipient_first_name"`
|
|
||||||
RecipientLastName string `json:"recipient_last_name"`
|
type ExpensesListRetrieved ExpensesListResponse
|
||||||
Amount int `json:"amount"`
|
|
||||||
Currency string `json:"currency"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
// {{{ Entity
|
// {{{ Entity
|
||||||
@ -91,18 +72,6 @@ type ExpenseEntity struct {
|
|||||||
Place string
|
Place string
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExpenseUpdateEntity struct {
|
|
||||||
ID int
|
|
||||||
UpdatedAt time.Time
|
|
||||||
|
|
||||||
Amount int
|
|
||||||
Currency string
|
|
||||||
|
|
||||||
// Expense Detail
|
|
||||||
Name string
|
|
||||||
Place string
|
|
||||||
}
|
|
||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
// {{{ Domain Models
|
// {{{ Domain Models
|
||||||
|
|
||||||
|
@ -37,22 +37,26 @@ type EventRepository interface {
|
|||||||
GetByID(ctx context.Context, eventID int) (*model.EventRetrieved, error)
|
GetByID(ctx context.Context, eventID int) (*model.EventRetrieved, error)
|
||||||
|
|
||||||
// related to events of a user
|
// related to events of a user
|
||||||
ListEventsByUserID(ctx context.Context, userID int) ([]model.EventListRetrieved, error)
|
ListEventsByUserID(ctx context.Context, userID int) ([]*model.EventListRetrieved, error)
|
||||||
|
|
||||||
// CheckParticipation(ctx context.Context, userID, eventID int) error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExpenseRepository interface {
|
type ExpenseRepository interface {
|
||||||
DeleteExpense(ctx context.Context, expenseID int) error
|
Create()
|
||||||
DeleteTransactionsOfExpense(ctx context.Context, expenseID int) error
|
Update()
|
||||||
GetExpenseByID(ctx context.Context, expenseID int) (*model.ExpenseRetrieved, error)
|
Delete() // Delete also the related transactions
|
||||||
InsertExpense(
|
|
||||||
ctx context.Context,
|
ListExpensesByUserID()
|
||||||
expenseEntity *model.ExpenseEntity,
|
GetByID()
|
||||||
) (*model.ExpenseEntity, error)
|
}
|
||||||
ListExpensesByEventID(ctx context.Context, id int) ([]model.ExpensesListRetrieved, error)
|
|
||||||
UpdateExpenseByID(
|
type ParticipationRepository interface {
|
||||||
ctx context.Context,
|
Create()
|
||||||
expenseUpdate *model.ExpenseUpdateEntity,
|
Delete()
|
||||||
) (*model.ExpenseEntity, error)
|
CheckParticipation(ctx context.Context, userID, eventID int) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type TransactionRepository interface {
|
||||||
|
Create()
|
||||||
|
// Delete() might be handled in the Expense
|
||||||
|
// Transaction is a joined entity, we don't provide diret read operation
|
||||||
}
|
}
|
||||||
|
@ -33,9 +33,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type eventUsecase struct {
|
type eventUsecase struct {
|
||||||
userUC User
|
userUC User
|
||||||
eventRepo repo.EventRepository
|
eventRepo repo.EventRepository
|
||||||
expenseRepo repo.ExpenseRepository
|
expenseRepo repo.ExpenseRepository
|
||||||
|
participationRepo repo.ParticipationRepository
|
||||||
|
transactionRepo repo.TransactionRepository
|
||||||
|
|
||||||
dbRepo repo.DBRepository
|
dbRepo repo.DBRepository
|
||||||
}
|
}
|
||||||
@ -53,9 +55,11 @@ func NewEventUsecase(
|
|||||||
uuc User,
|
uuc User,
|
||||||
ev repo.EventRepository,
|
ev repo.EventRepository,
|
||||||
ex repo.ExpenseRepository,
|
ex repo.ExpenseRepository,
|
||||||
|
pa repo.ParticipationRepository, // XXX: Might be handled in event
|
||||||
|
tr repo.TransactionRepository, // XXX: Might be handled in event
|
||||||
db repo.DBRepository,
|
db repo.DBRepository,
|
||||||
) Event {
|
) Event {
|
||||||
return &eventUsecase{uuc, ev, ex, db}
|
return &eventUsecase{uuc, ev, ex, pa, tr, db}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (evuc *eventUsecase) CreateEvent(
|
func (evuc *eventUsecase) CreateEvent(
|
||||||
@ -129,10 +133,10 @@ func (evuc *eventUsecase) GetEventDetail(
|
|||||||
userID, eventID int,
|
userID, eventID int,
|
||||||
) (*model.EventInfoResponse, error) {
|
) (*model.EventInfoResponse, error) {
|
||||||
// Check if the user has the right to get this event
|
// Check if the user has the right to get this event
|
||||||
// err := evuc.participationRepo.CheckParticipation(ctx, userID, eventID)
|
err := evuc.participationRepo.CheckParticipation(ctx, userID, eventID)
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// return nil, ErrNoParticipation
|
return nil, ErrNoParticipation
|
||||||
// }
|
}
|
||||||
|
|
||||||
// Get the eventDetail
|
// Get the eventDetail
|
||||||
// TODO: This can also be put into the cache
|
// TODO: This can also be put into the cache
|
||||||
|
Loading…
x
Reference in New Issue
Block a user