feat: implement repo expense methods
All checks were successful
Build and test / Build (push) Successful in 2m28s
All checks were successful
Build and test / Build (push) Successful in 2m28s
This commit is contained in:
parent
58cff774e6
commit
b30a5c5c2d
@ -35,7 +35,7 @@ import (
|
||||
)
|
||||
|
||||
type eventRepository struct {
|
||||
queries sqlc.Querier
|
||||
queries *sqlc.Queries
|
||||
}
|
||||
|
||||
func NewEventRepository(db *sql.DB) repo.EventRepository {
|
||||
|
@ -1,13 +1,75 @@
|
||||
// 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)
|
||||
@ -83,3 +145,87 @@ func convToExpenseRetrieved(expenseDTO *sqlc.GetExpenseByIDRow) (*model.ExpenseR
|
||||
|
||||
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,3 +1,25 @@
|
||||
// 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 (
|
||||
|
@ -24,14 +24,6 @@ FROM "expense" ex
|
||||
JOIN "event" ev ON ev.id = ex.event_id
|
||||
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
|
||||
WITH payer_transaction as (
|
||||
SELECT pt.expense_id,
|
||||
|
@ -183,47 +183,6 @@ func (q *Queries) ListExpensesByEventID(ctx context.Context, id int32) ([]Expens
|
||||
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
|
||||
UPDATE "expense"
|
||||
SET updated_at = $2, amount = $3, currency = $4, name = $5, place = $6
|
||||
|
@ -22,7 +22,6 @@ type Querier interface {
|
||||
InsertUser(ctx context.Context, arg InsertUserParams) (User, error)
|
||||
ListEventsByUserID(ctx context.Context, userID int32) ([]ListEventsByUserIDRow, error)
|
||||
ListExpensesByEventID(ctx context.Context, id int32) ([]Expense, error)
|
||||
ListExpensesByEventIDByUserID(ctx context.Context, id int32) ([]Expense, error)
|
||||
UpdateEventByID(ctx context.Context, arg UpdateEventByIDParams) error
|
||||
UpdateExpenseByID(ctx context.Context, arg UpdateExpenseByIDParams) (Expense, error)
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ import (
|
||||
)
|
||||
|
||||
type userRepository struct {
|
||||
querier sqlc.Querier
|
||||
querier *sqlc.Queries
|
||||
}
|
||||
|
||||
const insertTimeout = 1 * time.Second
|
||||
|
@ -37,7 +37,17 @@ type ExpenseRequest struct {
|
||||
// }}}
|
||||
// {{{ Response
|
||||
|
||||
type ExpensesListResponse struct {
|
||||
type (
|
||||
ExpenseGetResponse Expense
|
||||
ExpenseResponse ExpenseRetrieved
|
||||
)
|
||||
|
||||
// }}}
|
||||
// {{{ Retrieved
|
||||
|
||||
type ExpenseRetrieved Expense
|
||||
|
||||
type ExpensesListRetrieved struct {
|
||||
ID int `json:"id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
@ -48,16 +58,6 @@ type ExpensesListResponse struct {
|
||||
Detail ExpenseDetail `json:"detail"`
|
||||
}
|
||||
|
||||
type ExpenseGetResponse Expense
|
||||
|
||||
// }}}
|
||||
// {{{ Retrieved
|
||||
|
||||
type (
|
||||
ExpensesListRetrieved ExpensesListResponse
|
||||
ExpenseRetrieved Expense
|
||||
)
|
||||
|
||||
type PaymentRetrieved struct {
|
||||
PayerID int `json:"payer_id"`
|
||||
PayerFirstName string `json:"payer_first_name"`
|
||||
@ -91,6 +91,18 @@ type ExpenseEntity struct {
|
||||
Place string
|
||||
}
|
||||
|
||||
type ExpenseUpdateEntity struct {
|
||||
ID int
|
||||
UpdatedAt time.Time
|
||||
|
||||
Amount int
|
||||
Currency string
|
||||
|
||||
// Expense Detail
|
||||
Name string
|
||||
Place string
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ Domain Models
|
||||
|
||||
|
@ -38,25 +38,21 @@ type EventRepository interface {
|
||||
|
||||
// related to events of a user
|
||||
ListEventsByUserID(ctx context.Context, userID int) ([]model.EventListRetrieved, error)
|
||||
|
||||
// CheckParticipation(ctx context.Context, userID, eventID int) error
|
||||
}
|
||||
|
||||
type ExpenseRepository interface {
|
||||
Create()
|
||||
Update()
|
||||
Delete() // Delete also the related transactions
|
||||
|
||||
ListExpensesByUserID()
|
||||
GetByID()
|
||||
}
|
||||
|
||||
type ParticipationRepository interface {
|
||||
Create()
|
||||
Delete()
|
||||
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
|
||||
DeleteExpense(ctx context.Context, expenseID int) error
|
||||
DeleteTransactionsOfExpense(ctx context.Context, expenseID int) error
|
||||
GetExpenseByID(ctx context.Context, expenseID int) (*model.ExpenseRetrieved, error)
|
||||
InsertExpense(
|
||||
ctx context.Context,
|
||||
expenseEntity *model.ExpenseEntity,
|
||||
) (*model.ExpenseEntity, error)
|
||||
ListExpensesByEventID(ctx context.Context, id int) ([]model.ExpensesListRetrieved, error)
|
||||
UpdateExpenseByID(
|
||||
ctx context.Context,
|
||||
expenseUpdate *model.ExpenseUpdateEntity,
|
||||
) (*model.ExpenseEntity, error)
|
||||
}
|
||||
|
@ -36,8 +36,6 @@ type eventUsecase struct {
|
||||
userUC User
|
||||
eventRepo repo.EventRepository
|
||||
expenseRepo repo.ExpenseRepository
|
||||
participationRepo repo.ParticipationRepository
|
||||
transactionRepo repo.TransactionRepository
|
||||
|
||||
dbRepo repo.DBRepository
|
||||
}
|
||||
@ -55,11 +53,9 @@ func NewEventUsecase(
|
||||
uuc User,
|
||||
ev repo.EventRepository,
|
||||
ex repo.ExpenseRepository,
|
||||
pa repo.ParticipationRepository, // XXX: Might be handled in event
|
||||
tr repo.TransactionRepository, // XXX: Might be handled in event
|
||||
db repo.DBRepository,
|
||||
) Event {
|
||||
return &eventUsecase{uuc, ev, ex, pa, tr, db}
|
||||
return &eventUsecase{uuc, ev, ex, db}
|
||||
}
|
||||
|
||||
func (evuc *eventUsecase) CreateEvent(
|
||||
@ -133,10 +129,10 @@ func (evuc *eventUsecase) GetEventDetail(
|
||||
userID, eventID int,
|
||||
) (*model.EventInfoResponse, error) {
|
||||
// Check if the user has the right to get this event
|
||||
err := evuc.participationRepo.CheckParticipation(ctx, userID, eventID)
|
||||
if err != nil {
|
||||
return nil, ErrNoParticipation
|
||||
}
|
||||
// err := evuc.participationRepo.CheckParticipation(ctx, userID, eventID)
|
||||
// if err != nil {
|
||||
// return nil, ErrNoParticipation
|
||||
// }
|
||||
|
||||
// Get the eventDetail
|
||||
// TODO: This can also be put into the cache
|
||||
|
Loading…
Reference in New Issue
Block a user