Compare commits

...

4 Commits

Author SHA1 Message Date
Muyao CHEN
b30a5c5c2d feat: implement repo expense methods
All checks were successful
Build and test / Build (push) Successful in 2m28s
2024-10-24 23:39:13 +02:00
Muyao CHEN
58cff774e6 feat: add expense repo type conversion 2024-10-24 22:33:24 +02:00
Muyao CHEN
716a58d44c fix: use simple slices instead of []*T 2024-10-24 22:32:57 +02:00
Muyao CHEN
de7c6f7223 test: add event repo tests 2024-10-23 23:29:44 +02:00
13 changed files with 535 additions and 98 deletions

View File

@ -564,3 +564,16 @@ 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`.

View File

@ -35,7 +35,7 @@ import (
) )
type eventRepository struct { type eventRepository struct {
queries sqlc.Querier queries *sqlc.Queries
} }
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,10 +110,12 @@ func convToEventList(eventsDTO []sqlc.ListEventsByUserIDRow) ([]*model.EventList
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)
@ -126,7 +128,7 @@ func convToEventList(eventsDTO []sqlc.ListEventsByUserIDRow) ([]*model.EventList
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)

View File

@ -0,0 +1,122 @@
// 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)
}

View File

@ -0,0 +1,231 @@
// 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
}

View File

@ -0,0 +1,96 @@
// 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)
}

View File

@ -24,14 +24,6 @@ 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,

View File

@ -183,47 +183,6 @@ 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

View File

@ -22,7 +22,6 @@ 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)
} }

View File

@ -34,7 +34,7 @@ import (
) )
type userRepository struct { type userRepository struct {
querier sqlc.Querier querier *sqlc.Queries
} }
const insertTimeout = 1 * time.Second const insertTimeout = 1 * time.Second

View File

@ -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

View File

@ -37,7 +37,17 @@ type ExpenseRequest struct {
// }}} // }}}
// {{{ Response // {{{ Response
type ExpensesListResponse struct { type (
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"`
@ -48,12 +58,21 @@ type ExpensesListResponse struct {
Detail ExpenseDetail `json:"detail"` Detail ExpenseDetail `json:"detail"`
} }
type ExpenseGetResponse Expense type PaymentRetrieved struct {
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 {
// {{{ Retrieved RecipientID int `json:"recipient_id"`
RecipientFirstName string `json:"recipient_first_name"`
type ExpensesListRetrieved ExpensesListResponse RecipientLastName string `json:"recipient_last_name"`
Amount int `json:"amount"`
Currency string `json:"currency"`
}
// }}} // }}}
// {{{ Entity // {{{ Entity
@ -72,6 +91,18 @@ 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

View File

@ -37,26 +37,22 @@ 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 {
Create() DeleteExpense(ctx context.Context, expenseID int) error
Update() DeleteTransactionsOfExpense(ctx context.Context, expenseID int) error
Delete() // Delete also the related transactions GetExpenseByID(ctx context.Context, expenseID int) (*model.ExpenseRetrieved, error)
InsertExpense(
ListExpensesByUserID() ctx context.Context,
GetByID() expenseEntity *model.ExpenseEntity,
} ) (*model.ExpenseEntity, error)
ListExpensesByEventID(ctx context.Context, id int) ([]model.ExpensesListRetrieved, error)
type ParticipationRepository interface { UpdateExpenseByID(
Create() ctx context.Context,
Delete() expenseUpdate *model.ExpenseUpdateEntity,
CheckParticipation(ctx context.Context, userID, eventID int) error ) (*model.ExpenseEntity, error)
}
type TransactionRepository interface {
Create()
// Delete() might be handled in the Expense
// Transaction is a joined entity, we don't provide diret read operation
} }

View File

@ -36,8 +36,6 @@ 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
} }
@ -55,11 +53,9 @@ 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, pa, tr, db} return &eventUsecase{uuc, ev, ex, db}
} }
func (evuc *eventUsecase) CreateEvent( func (evuc *eventUsecase) CreateEvent(
@ -133,10 +129,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