From dd999b9355f9cf1097925d5d788a6248ec686c5e Mon Sep 17 00:00:00 2001 From: Muyao CHEN Date: Sat, 26 Oct 2024 17:27:33 +0200 Subject: [PATCH] refacto: refacto repo layer code while adding new usecase methods --- internal/howmuch/adapter/repo/db.go | 3 + internal/howmuch/adapter/repo/event.go | 81 ++++++++++++++++++- internal/howmuch/adapter/repo/expense.go | 18 ++--- .../adapter/repo/sqlc/participation.sql | 5 ++ .../adapter/repo/sqlc/participation.sql.go | 25 ++++++ internal/howmuch/adapter/repo/sqlc/querier.go | 1 + internal/howmuch/adapter/repo/user.go | 2 - internal/howmuch/model/event.go | 2 +- internal/howmuch/usecase/repo/event.go | 12 ++- internal/howmuch/usecase/usecase/event.go | 57 +++++++++++-- 10 files changed, 185 insertions(+), 21 deletions(-) diff --git a/internal/howmuch/adapter/repo/db.go b/internal/howmuch/adapter/repo/db.go index b949459..0e57471 100644 --- a/internal/howmuch/adapter/repo/db.go +++ b/internal/howmuch/adapter/repo/db.go @@ -25,6 +25,7 @@ package repo import ( "context" "database/sql" + "time" "git.vinchent.xyz/vinchent/howmuch/internal/howmuch/adapter/repo/sqlc" "git.vinchent.xyz/vinchent/howmuch/internal/howmuch/usecase/repo" @@ -35,6 +36,8 @@ type dbRepository struct { db *sql.DB } +const queryTimeout = 3 * time.Second + func NewDBRepository(db *sql.DB) repo.DBRepository { return &dbRepository{ db: db, diff --git a/internal/howmuch/adapter/repo/event.go b/internal/howmuch/adapter/repo/event.go index 4944a45..5422090 100644 --- a/internal/howmuch/adapter/repo/event.go +++ b/internal/howmuch/adapter/repo/event.go @@ -26,6 +26,7 @@ import ( "context" "database/sql" "encoding/json" + "errors" "time" "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) { - var events []model.EventListRetrieved + events := make([]model.EventListRetrieved, len(eventsDTO)) - for _, evDTO := range eventsDTO { + for i, evDTO := range eventsDTO { var owner model.UserBaseRetrieved err := json.Unmarshal(evDTO.Owner, &owner) if err != nil { @@ -154,7 +155,7 @@ func convToEventList(eventsDTO []sqlc.ListEventsByUserIDRow) ([]model.EventListR CreatedAt: evDTO.CreatedAt, } - events = append(events, ev) + events[i] = ev } return events, nil @@ -199,3 +200,77 @@ func (e *eventRepository) UpdateEventByID( }) 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 +} diff --git a/internal/howmuch/adapter/repo/expense.go b/internal/howmuch/adapter/repo/expense.go index 2fce177..95a5f04 100644 --- a/internal/howmuch/adapter/repo/expense.go +++ b/internal/howmuch/adapter/repo/expense.go @@ -97,15 +97,15 @@ func convToPayments(raw json.RawMessage) ([]model.Payment, error) { return nil, err } - var payments []model.Payment - for _, p := range paymentsRetrieved { + payments := make([]model.Payment, len(paymentsRetrieved)) + for i, 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) + payments[i] = payment } return payments, nil @@ -120,15 +120,15 @@ func convToBenefits(raw json.RawMessage) ([]model.Benefit, error) { return nil, err } - var benefits []model.Benefit - for _, b := range benefitsRetrieved { + benefits := make([]model.Benefit, len(benefitsRetrieved)) + for i, 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) + benefits[i] = benefit } return benefits, nil @@ -214,8 +214,8 @@ func (e *expenseRepository) ListExpensesByEventID( if err != nil { return nil, err } - var res []model.ExpensesListRetrieved - for _, dto := range listDTO { + res := make([]model.ExpensesListRetrieved, len(listDTO)) + for i, dto := range listDTO { elem := model.ExpensesListRetrieved{ ID: int(dto.ID), CreatedAt: dto.CreatedAt, @@ -227,7 +227,7 @@ func (e *expenseRepository) ListExpensesByEventID( Place: dto.Place.String, }, } - res = append(res, elem) + res[i] = elem } return res, nil } diff --git a/internal/howmuch/adapter/repo/sqlc/participation.sql b/internal/howmuch/adapter/repo/sqlc/participation.sql index 6798a40..5b1dfe0 100644 --- a/internal/howmuch/adapter/repo/sqlc/participation.sql +++ b/internal/howmuch/adapter/repo/sqlc/participation.sql @@ -3,3 +3,8 @@ INSERT INTO participation ( user_id, event_id, invited_by_user_id, created_at, updated_at ) VALUES ($1, $2, $3, $4, $5) 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; diff --git a/internal/howmuch/adapter/repo/sqlc/participation.sql.go b/internal/howmuch/adapter/repo/sqlc/participation.sql.go index 3cc969b..4612c2d 100644 --- a/internal/howmuch/adapter/repo/sqlc/participation.sql.go +++ b/internal/howmuch/adapter/repo/sqlc/participation.sql.go @@ -11,6 +11,31 @@ import ( "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 INSERT INTO participation ( user_id, event_id, invited_by_user_id, created_at, updated_at diff --git a/internal/howmuch/adapter/repo/sqlc/querier.go b/internal/howmuch/adapter/repo/sqlc/querier.go index 4a7161b..bd533fe 100644 --- a/internal/howmuch/adapter/repo/sqlc/querier.go +++ b/internal/howmuch/adapter/repo/sqlc/querier.go @@ -13,6 +13,7 @@ type Querier interface { DeleteTransactionsOfExpenseID(ctx context.Context, expenseID int32) error GetEventByID(ctx context.Context, id int32) (GetEventByIDRow, 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) GetUserByID(ctx context.Context, id int32) (User, error) InsertEvent(ctx context.Context, arg InsertEventParams) (Event, error) diff --git a/internal/howmuch/adapter/repo/user.go b/internal/howmuch/adapter/repo/user.go index 2a32fb7..d7ee831 100644 --- a/internal/howmuch/adapter/repo/user.go +++ b/internal/howmuch/adapter/repo/user.go @@ -37,8 +37,6 @@ type userRepository struct { queries *sqlc.Queries } -const queryTimeout = 3 * time.Second - func NewUserRepository(db *sql.DB) repo.UserRepository { return &userRepository{ queries: sqlc.New(db), diff --git a/internal/howmuch/model/event.go b/internal/howmuch/model/event.go index 19703a7..8c33684 100644 --- a/internal/howmuch/model/event.go +++ b/internal/howmuch/model/event.go @@ -36,7 +36,7 @@ type EventCreateRequest struct { // }}} // {{{ Response View Object (from service to controller) -type EventBaseItemResponse struct { +type EventListResponse struct { ID int Name string Description string diff --git a/internal/howmuch/usecase/repo/event.go b/internal/howmuch/usecase/repo/event.go index 4a3b888..9ae7a48 100644 --- a/internal/howmuch/usecase/repo/event.go +++ b/internal/howmuch/usecase/repo/event.go @@ -39,7 +39,17 @@ type EventRepository interface { // related to events of a user 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 { diff --git a/internal/howmuch/usecase/usecase/event.go b/internal/howmuch/usecase/usecase/event.go index 6b745c0..5be64e5 100644 --- a/internal/howmuch/usecase/usecase/event.go +++ b/internal/howmuch/usecase/usecase/event.go @@ -74,13 +74,37 @@ func (evuc *eventUsecase) CreateEvent( data, err := evuc.dbRepo.Transaction( ctx, - func(txCtx context.Context, tx interface{}) (interface{}, error) { - // Create + func(txCtx context.Context, tx any) (any, error) { + // Create the event created, err := evuc.eventRepo.Create(ctx, evEntity, tx) if err != nil { 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. log.InfoLog( "created new event", @@ -90,6 +114,7 @@ func (evuc *eventUsecase) CreateEvent( created.OwnerID, ) + // Construct the response ownerResponse, err := evuc.userUC.GetUserBaseResponseByID(ctx, created.OwnerID) if err != nil { return nil, err @@ -106,9 +131,11 @@ func (evuc *eventUsecase) CreateEvent( Owner: ownerResponse, CreatedAt: created.CreatedAt, UpdatedAt: created.UpdatedAt, + Users: []model.UserBaseResponse{*ownerResponse}, } return evResponse, err - }) + }, + ) if err != nil { return nil, err } @@ -121,8 +148,28 @@ func (evuc *eventUsecase) CreateEvent( func (evuc *eventUsecase) ListEvents( ctx context.Context, userID int, -) ([]model.EventBaseItemResponse, error) { - return nil, nil +) ([]model.EventListResponse, error) { + 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