refacto: refacto repo layer code while adding new usecase methods
All checks were successful
Build and test / Build (push) Successful in 2m19s

This commit is contained in:
Muyao CHEN 2024-10-26 17:27:33 +02:00
parent 14ee642aab
commit dd999b9355
10 changed files with 185 additions and 21 deletions

View File

@ -25,6 +25,7 @@ package repo
import ( import (
"context" "context"
"database/sql" "database/sql"
"time"
"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/adapter/repo/sqlc" "git.vinchent.xyz/vinchent/howmuch/internal/howmuch/adapter/repo/sqlc"
"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/usecase/repo" "git.vinchent.xyz/vinchent/howmuch/internal/howmuch/usecase/repo"
@ -35,6 +36,8 @@ type dbRepository struct {
db *sql.DB db *sql.DB
} }
const queryTimeout = 3 * time.Second
func NewDBRepository(db *sql.DB) repo.DBRepository { func NewDBRepository(db *sql.DB) repo.DBRepository {
return &dbRepository{ return &dbRepository{
db: db, db: db,

View File

@ -26,6 +26,7 @@ import (
"context" "context"
"database/sql" "database/sql"
"encoding/json" "encoding/json"
"errors"
"time" "time"
"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/adapter/repo/sqlc" "git.vinchent.xyz/vinchent/howmuch/internal/howmuch/adapter/repo/sqlc"
@ -136,9 +137,9 @@ func (e *eventRepository) GetByID(
} }
func convToEventList(eventsDTO []sqlc.ListEventsByUserIDRow) ([]model.EventListRetrieved, error) { func convToEventList(eventsDTO []sqlc.ListEventsByUserIDRow) ([]model.EventListRetrieved, error) {
var events []model.EventListRetrieved events := make([]model.EventListRetrieved, len(eventsDTO))
for _, evDTO := range eventsDTO { for i, evDTO := range eventsDTO {
var owner model.UserBaseRetrieved var owner model.UserBaseRetrieved
err := json.Unmarshal(evDTO.Owner, &owner) err := json.Unmarshal(evDTO.Owner, &owner)
if err != nil { if err != nil {
@ -154,7 +155,7 @@ func convToEventList(eventsDTO []sqlc.ListEventsByUserIDRow) ([]model.EventListR
CreatedAt: evDTO.CreatedAt, CreatedAt: evDTO.CreatedAt,
} }
events = append(events, ev) events[i] = ev
} }
return events, nil return events, nil
@ -199,3 +200,77 @@ func (e *eventRepository) UpdateEventByID(
}) })
return err return err
} }
// GetParticipation implements repo.EventRepository.
func (e *eventRepository) GetParticipation(
ctx context.Context,
userID, eventID int,
tx any,
) (*model.ParticipationEntity, error) {
timeoutCtx, cancel := context.WithTimeout(ctx, queryTimeout)
defer cancel()
queries := getQueries(e.queries, tx)
partDTO, err := queries.GetParticipation(timeoutCtx, sqlc.GetParticipationParams{
UserID: int32(userID),
EventID: int32(eventID),
})
if errors.Is(err, sql.ErrNoRows) {
// No error, but participation not found
return nil, nil
}
if err != nil {
return nil, err
}
return &model.ParticipationEntity{
ID: int(partDTO.ID),
UserID: int(partDTO.UserID),
EventID: int(partDTO.EventID),
InvitedByUserID: int(partDTO.InvitedByUserID.Int32),
CreatedAt: partDTO.CreatedAt,
UpdatedAt: partDTO.UpdatedAt,
}, nil
}
// InsertParticipation implements repo.EventRepository.
func (e *eventRepository) InsertParticipation(
ctx context.Context,
userID int,
eventID int,
invitedByUserID int,
tx any,
) (*model.ParticipationEntity, error) {
timeoutCtx, cancel := context.WithTimeout(ctx, queryTimeout)
defer cancel()
queries := getQueries(e.queries, tx)
var invitedBy sql.NullInt32
if invitedByUserID == 0 {
invitedBy = sql.NullInt32{Int32: 0, Valid: false}
} else {
invitedBy = sql.NullInt32{Int32: int32(invitedByUserID), Valid: true}
}
participationDTO, err := queries.InsertParticipation(timeoutCtx, sqlc.InsertParticipationParams{
UserID: int32(userID),
EventID: int32(eventID),
InvitedByUserID: invitedBy,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
})
if err != nil {
return nil, err
}
return &model.ParticipationEntity{
ID: int(participationDTO.ID),
UserID: int(participationDTO.UserID),
EventID: int(participationDTO.EventID),
InvitedByUserID: int(participationDTO.InvitedByUserID.Int32),
CreatedAt: participationDTO.CreatedAt,
UpdatedAt: participationDTO.UpdatedAt,
}, nil
}

View File

@ -97,15 +97,15 @@ func convToPayments(raw json.RawMessage) ([]model.Payment, error) {
return nil, err return nil, err
} }
var payments []model.Payment payments := make([]model.Payment, len(paymentsRetrieved))
for _, p := range paymentsRetrieved { for i, p := range paymentsRetrieved {
payment := model.Payment{ payment := model.Payment{
PayerID: p.PayerID, PayerID: p.PayerID,
PayerFirstName: p.PayerFirstName, PayerFirstName: p.PayerFirstName,
PayerLastName: p.PayerLastName, PayerLastName: p.PayerLastName,
Amount: model.MakeMoney(p.Amount, model.Currency(p.Currency)), Amount: model.MakeMoney(p.Amount, model.Currency(p.Currency)),
} }
payments = append(payments, payment) payments[i] = payment
} }
return payments, nil return payments, nil
@ -120,15 +120,15 @@ func convToBenefits(raw json.RawMessage) ([]model.Benefit, error) {
return nil, err return nil, err
} }
var benefits []model.Benefit benefits := make([]model.Benefit, len(benefitsRetrieved))
for _, b := range benefitsRetrieved { for i, b := range benefitsRetrieved {
benefit := model.Benefit{ benefit := model.Benefit{
RecipientID: b.RecipientID, RecipientID: b.RecipientID,
RecipientFirstName: b.RecipientFirstName, RecipientFirstName: b.RecipientFirstName,
RecipientLastName: b.RecipientLastName, RecipientLastName: b.RecipientLastName,
Amount: model.MakeMoney(b.Amount, model.Currency(b.Currency)), Amount: model.MakeMoney(b.Amount, model.Currency(b.Currency)),
} }
benefits = append(benefits, benefit) benefits[i] = benefit
} }
return benefits, nil return benefits, nil
@ -214,8 +214,8 @@ func (e *expenseRepository) ListExpensesByEventID(
if err != nil { if err != nil {
return nil, err return nil, err
} }
var res []model.ExpensesListRetrieved res := make([]model.ExpensesListRetrieved, len(listDTO))
for _, dto := range listDTO { for i, dto := range listDTO {
elem := model.ExpensesListRetrieved{ elem := model.ExpensesListRetrieved{
ID: int(dto.ID), ID: int(dto.ID),
CreatedAt: dto.CreatedAt, CreatedAt: dto.CreatedAt,
@ -227,7 +227,7 @@ func (e *expenseRepository) ListExpensesByEventID(
Place: dto.Place.String, Place: dto.Place.String,
}, },
} }
res = append(res, elem) res[i] = elem
} }
return res, nil return res, nil
} }

View File

@ -3,3 +3,8 @@ INSERT INTO participation (
user_id, event_id, invited_by_user_id, created_at, updated_at user_id, event_id, invited_by_user_id, created_at, updated_at
) VALUES ($1, $2, $3, $4, $5) ) VALUES ($1, $2, $3, $4, $5)
RETURNING *; RETURNING *;
-- name: GetParticipation :one
SELECT id, user_id, event_id, invited_by_user_id, created_at, updated_at
FROM "participation"
WHERE user_id = $1 AND event_id = $2;

View File

@ -11,6 +11,31 @@ import (
"time" "time"
) )
const getParticipation = `-- name: GetParticipation :one
SELECT id, user_id, event_id, invited_by_user_id, created_at, updated_at
FROM "participation"
WHERE user_id = $1 AND event_id = $2
`
type GetParticipationParams struct {
UserID int32
EventID int32
}
func (q *Queries) GetParticipation(ctx context.Context, arg GetParticipationParams) (Participation, error) {
row := q.db.QueryRowContext(ctx, getParticipation, arg.UserID, arg.EventID)
var i Participation
err := row.Scan(
&i.ID,
&i.UserID,
&i.EventID,
&i.InvitedByUserID,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const insertParticipation = `-- name: InsertParticipation :one const insertParticipation = `-- name: InsertParticipation :one
INSERT INTO participation ( INSERT INTO participation (
user_id, event_id, invited_by_user_id, created_at, updated_at user_id, event_id, invited_by_user_id, created_at, updated_at

View File

@ -13,6 +13,7 @@ type Querier interface {
DeleteTransactionsOfExpenseID(ctx context.Context, expenseID int32) error DeleteTransactionsOfExpenseID(ctx context.Context, expenseID int32) error
GetEventByID(ctx context.Context, id int32) (GetEventByIDRow, error) GetEventByID(ctx context.Context, id int32) (GetEventByIDRow, error)
GetExpenseByID(ctx context.Context, id int32) (GetExpenseByIDRow, error) GetExpenseByID(ctx context.Context, id int32) (GetExpenseByIDRow, error)
GetParticipation(ctx context.Context, arg GetParticipationParams) (Participation, error)
GetUserByEmail(ctx context.Context, email string) (User, error) GetUserByEmail(ctx context.Context, email string) (User, error)
GetUserByID(ctx context.Context, id int32) (User, error) GetUserByID(ctx context.Context, id int32) (User, error)
InsertEvent(ctx context.Context, arg InsertEventParams) (Event, error) InsertEvent(ctx context.Context, arg InsertEventParams) (Event, error)

View File

@ -37,8 +37,6 @@ type userRepository struct {
queries *sqlc.Queries queries *sqlc.Queries
} }
const queryTimeout = 3 * time.Second
func NewUserRepository(db *sql.DB) repo.UserRepository { func NewUserRepository(db *sql.DB) repo.UserRepository {
return &userRepository{ return &userRepository{
queries: sqlc.New(db), queries: sqlc.New(db),

View File

@ -36,7 +36,7 @@ type EventCreateRequest struct {
// }}} // }}}
// {{{ Response View Object (from service to controller) // {{{ Response View Object (from service to controller)
type EventBaseItemResponse struct { type EventListResponse struct {
ID int ID int
Name string Name string
Description string Description string

View File

@ -39,7 +39,17 @@ type EventRepository interface {
// related to events of a user // related to events of a user
ListEventsByUserID(ctx context.Context, userID int, tx any) ([]model.EventListRetrieved, error) ListEventsByUserID(ctx context.Context, userID int, tx any) ([]model.EventListRetrieved, error)
// CheckParticipation(ctx context.Context, userID, eventID int) error InsertParticipation(
ctx context.Context,
userID, eventID, invitedByUserID int,
tx any,
) (*model.ParticipationEntity, error)
GetParticipation(
ctx context.Context,
userID, eventID int,
tx any,
) (*model.ParticipationEntity, error)
} }
type ExpenseRepository interface { type ExpenseRepository interface {

View File

@ -74,13 +74,37 @@ func (evuc *eventUsecase) CreateEvent(
data, err := evuc.dbRepo.Transaction( data, err := evuc.dbRepo.Transaction(
ctx, ctx,
func(txCtx context.Context, tx interface{}) (interface{}, error) { func(txCtx context.Context, tx any) (any, error) {
// Create // Create the event
created, err := evuc.eventRepo.Create(ctx, evEntity, tx) created, err := evuc.eventRepo.Create(ctx, evEntity, tx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// participate to the event
participation, err := evuc.eventRepo.InsertParticipation(
ctx,
created.OwnerID,
created.ID,
0,
tx,
)
if err != nil {
return nil, err
}
if participation == nil {
// Unexpected error
log.ErrorLog(
"participation existed for event-user pair",
"userID",
created.OwnerID,
"eventID",
created.ID,
)
return nil, errno.InternalServerErr
}
// TODO: App log, maybe can be sent to some third party service. // TODO: App log, maybe can be sent to some third party service.
log.InfoLog( log.InfoLog(
"created new event", "created new event",
@ -90,6 +114,7 @@ func (evuc *eventUsecase) CreateEvent(
created.OwnerID, created.OwnerID,
) )
// Construct the response
ownerResponse, err := evuc.userUC.GetUserBaseResponseByID(ctx, created.OwnerID) ownerResponse, err := evuc.userUC.GetUserBaseResponseByID(ctx, created.OwnerID)
if err != nil { if err != nil {
return nil, err return nil, err
@ -106,9 +131,11 @@ func (evuc *eventUsecase) CreateEvent(
Owner: ownerResponse, Owner: ownerResponse,
CreatedAt: created.CreatedAt, CreatedAt: created.CreatedAt,
UpdatedAt: created.UpdatedAt, UpdatedAt: created.UpdatedAt,
Users: []model.UserBaseResponse{*ownerResponse},
} }
return evResponse, err return evResponse, err
}) },
)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -121,8 +148,28 @@ func (evuc *eventUsecase) CreateEvent(
func (evuc *eventUsecase) ListEvents( func (evuc *eventUsecase) ListEvents(
ctx context.Context, ctx context.Context,
userID int, userID int,
) ([]model.EventBaseItemResponse, error) { ) ([]model.EventListResponse, error) {
return nil, nil eventListRetrieved, err := evuc.eventRepo.ListEventsByUserID(ctx, userID, nil)
if err != nil {
return nil, err
}
// Check if the user is a member of the event
responses := make([]model.EventListResponse, len(eventListRetrieved))
for i, retrieved := range eventListRetrieved {
ownner := model.UserBaseResponse(*retrieved.Owner)
res := model.EventListResponse{
ID: retrieved.ID,
Name: retrieved.Name,
Description: retrieved.Description,
Owner: &ownner,
CreatedAt: retrieved.CreatedAt,
}
responses[i] = res
}
return responses, nil
} }
// GetEventDetail // GetEventDetail