Compare commits

...

4 Commits

Author SHA1 Message Date
Muyao CHEN
0da8b29507 test: try to test user repo and event repo
All checks were successful
Build and test / Build (push) Successful in 2m27s
2024-10-23 09:54:00 +02:00
Muyao CHEN
304651e7ff feat: implement event repo methods 2024-10-20 23:27:17 +02:00
Muyao CHEN
74ae6b7877 doc: add readme 2024-10-20 23:26:45 +02:00
Muyao CHEN
b4259e9a51 doc: add licence for event.go 2024-10-20 21:27:26 +02:00
15 changed files with 164 additions and 402 deletions

View File

@ -46,7 +46,7 @@ format: # format code.
.PHONY: add-copyright .PHONY: add-copyright
add-copyright: # add license to file headers. add-copyright: # add license to file headers.
@addlicense -v -f $(ROOT_DIR)/LICENSE $(ROOT_DIR) --skip-files=database.yml --skip-dirs=$(OUTPUT_DIR),deployment,migrations,configs,sqlc,web @addlicense -v -f $(ROOT_DIR)/LICENSE $(ROOT_DIR) --skip-files=database.yml --skip-dirs=$(OUTPUT_DIR),deployment,migrations,configs,sqlc,web,mock
.PHONY: swagger .PHONY: swagger
swagger: # Run swagger. swagger: # Run swagger.

View File

@ -521,3 +521,46 @@ is on my future learning plan!
_I found it quite interesting that simply with SQL, we can simulate the most _I found it quite interesting that simply with SQL, we can simulate the most
business logic. It is a must-have competence for software design and business logic. It is a must-have competence for software design and
development._ development._
### 2024/10/20
I was thinking that I should write test for `sqlc` generated code. And then
I found out `gomock` and see how it is done in the project of
`techschoo/simplebank`. It's a great tutorial project. It makes me questioning
my own project's structure. It seems overwhelmed at least at the repo level.
I don't actually use the sqlc generated object, instead I do a conversion to
my `Retrieved` objects. But with some advanced configuration we could make the
output of sqlc object directly usable. That will save a lot of code.
The problem I saw here is the dependency on `sqlc/models`, and the model
designed there has no business logic. Everything is done in the handlers
and the handlers query directly the DB.
More concretely, `sqlc` generates `RawJSON` for some fields that are embedded
structs. So I have to do the translation somewhere.
So I will just stick to the plan and keep going with the predefined structure.
I have to figure out how to use the generated mock files.
The goals for the next week is to finish the basic operations for each level
and run some integration tests with `curl`.
### 2024/10/22
I am facing come difficulties on testing of the `repo` functions.
First, I have to keep the business logic in the service layer. That means I
have to create the transaction at the service layer. I don't need to depend
on the implementation detail. So I have created a Transaction interface.
I don't care of the type of `tx` because I will pass it to repo layer and I
suppose that it knows what it is doing. Considering this, my repo `Create`
function will have to take an any and deduct the type of `tx`. So the layer
becomes untestable, because I have to pass a *sql.Tx into it and create a
querier.
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.
I'm not testing the whole thing but I test what I can.

1
go.mod
View File

@ -16,7 +16,6 @@ require (
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.19.0 github.com/spf13/viper v1.19.0
github.com/stretchr/testify v1.9.0 github.com/stretchr/testify v1.9.0
go.uber.org/mock v0.5.0
go.uber.org/zap v1.27.0 go.uber.org/zap v1.27.0
golang.org/x/crypto v0.27.0 golang.org/x/crypto v0.27.0
golang.org/x/net v0.26.0 golang.org/x/net v0.26.0

2
go.sum
View File

@ -142,8 +142,6 @@ github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65E
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=

View File

@ -1,9 +1,32 @@
// 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 package repo
import ( import (
"context" "context"
"database/sql" "database/sql"
"encoding/json" "encoding/json"
"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/model" "git.vinchent.xyz/vinchent/howmuch/internal/howmuch/model"
@ -12,12 +35,12 @@ import (
) )
type eventRepository struct { type eventRepository struct {
db *sql.DB queries sqlc.Querier
} }
func NewEventRepository(db *sql.DB) repo.EventRepository { func NewEventRepository(db *sql.DB) repo.EventRepository {
return &eventRepository{ return &eventRepository{
db: db, queries: sqlc.New(db),
} }
} }
@ -29,23 +52,10 @@ func (e *eventRepository) Create(
panic("unimplemented") panic("unimplemented")
} }
// Delete implements repo.EventRepository. func convToEventRetrieved(eventDTO *sqlc.GetEventByIDRow) (*model.EventRetrieved, error) {
func (e *eventRepository) Delete() {
panic("unimplemented")
}
// GetByID implements repo.EventRepository.
func (e *eventRepository) GetByID(ctx context.Context, eventID int) (*model.EventRetrieved, error) {
queries := sqlc.New(e.db)
eventDTO, err := queries.GetEventByID(ctx, int32(eventID))
if err != nil {
log.ErrorLog("query error", "err", err)
return nil, err
}
// marshal owner and users // marshal owner and users
var owner *model.UserBaseRetrieved var owner model.UserBaseRetrieved
err = json.Unmarshal(eventDTO.Owner, owner) err := json.Unmarshal(eventDTO.Owner, &owner)
if err != nil { if err != nil {
// Unexpected // Unexpected
log.ErrorLog("json unmarshal error", "err", err) log.ErrorLog("json unmarshal error", "err", err)
@ -53,7 +63,7 @@ func (e *eventRepository) GetByID(ctx context.Context, eventID int) (*model.Even
} }
var users []*model.UserBaseRetrieved var users []*model.UserBaseRetrieved
err = json.Unmarshal(eventDTO.Owner, &users) err = json.Unmarshal(eventDTO.Users, &users)
if err != nil { if err != nil {
// Unexpected // Unexpected
log.ErrorLog("json unmarshal error", "err", err) log.ErrorLog("json unmarshal error", "err", err)
@ -71,27 +81,71 @@ func (e *eventRepository) GetByID(ctx context.Context, eventID int) (*model.Even
DefaultCurrency: model.Currency(eventDTO.DefaultCurrency), DefaultCurrency: model.Currency(eventDTO.DefaultCurrency),
CreatedAt: eventDTO.CreatedAt, CreatedAt: eventDTO.CreatedAt,
UpdatedAt: eventDTO.UpdatedAt, UpdatedAt: eventDTO.UpdatedAt,
Owner: owner, Owner: &owner,
Users: users, Users: users,
} }
return eventRetrieved, nil return eventRetrieved, nil
} }
// GetByID implements repo.EventRepository.
func (e *eventRepository) GetByID(ctx context.Context, eventID int) (*model.EventRetrieved, error) {
eventDTO, err := e.queries.GetEventByID(ctx, int32(eventID))
if err != nil {
log.ErrorLog("query error", "err", err)
return nil, err
}
return convToEventRetrieved(&eventDTO)
}
func convToEventList(eventsDTO []sqlc.ListEventsByUserIDRow) ([]*model.EventListRetrieved, error) {
var events []*model.EventListRetrieved
for _, evDTO := range eventsDTO {
var owner model.UserBaseRetrieved
err := json.Unmarshal(evDTO.Owner, &owner)
if err != nil {
// Unexpected
log.ErrorLog("json unmarshal error", "err", err)
return nil, err
}
ev := &model.EventListRetrieved{
ID: int(evDTO.ID),
Name: evDTO.Name,
Description: evDTO.Description.String,
}
events = append(events, ev)
}
return events, nil
}
// ListEventsByUserID implements repo.EventRepository. // ListEventsByUserID implements repo.EventRepository.
func (e *eventRepository) ListEventsByUserID( func (e *eventRepository) ListEventsByUserID(
ctx context.Context, ctx context.Context,
userID int, userID int,
) ([]model.EventBaseItemEntity, error) { ) ([]*model.EventListRetrieved, error) {
panic("unimplemented") eventsDTO, err := e.queries.ListEventsByUserID(ctx, int32(userID))
} if err != nil {
log.ErrorLog("query error", "err", err)
return nil, err
}
// ListExpensesByUserID implements repo.EventRepository. return convToEventList(eventsDTO)
func (e *eventRepository) ListExpensesByUserID() {
panic("unimplemented")
} }
// UpdateInfo implements repo.EventRepository. // UpdateInfo implements repo.EventRepository.
func (e *eventRepository) UpdateInfo() { func (e *eventRepository) UpdateEventByID(
panic("unimplemented") ctx context.Context,
event *model.EventUpdateEntity,
) error {
err := e.queries.UpdateEventByID(ctx, sqlc.UpdateEventByIDParams{
ID: int32(event.ID),
Name: event.Name,
Description: sql.NullString{String: event.Description, Valid: true},
UpdatedAt: time.Now(),
})
return err
} }

View File

@ -1,277 +0,0 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: ./internal/howmuch/adapter/repo/sqlc/querier.go
//
// Generated by this command:
//
// mockgen -source=./internal/howmuch/adapter/repo/sqlc/querier.go -package=mock
//
// Package mock is a generated GoMock package.
package mock
import (
context "context"
reflect "reflect"
sqlc "git.vinchent.xyz/vinchent/howmuch/internal/howmuch/adapter/repo/sqlc"
gomock "go.uber.org/mock/gomock"
)
// MockQuerier is a mock of Querier interface.
type MockQuerier struct {
ctrl *gomock.Controller
recorder *MockQuerierMockRecorder
}
// MockQuerierMockRecorder is the mock recorder for MockQuerier.
type MockQuerierMockRecorder struct {
mock *MockQuerier
}
// NewMockQuerier creates a new mock instance.
func NewMockQuerier(ctrl *gomock.Controller) *MockQuerier {
mock := &MockQuerier{ctrl: ctrl}
mock.recorder = &MockQuerierMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockQuerier) EXPECT() *MockQuerierMockRecorder {
return m.recorder
}
// DeleteExpense mocks base method.
func (m *MockQuerier) DeleteExpense(ctx context.Context, id int32) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DeleteExpense", ctx, id)
ret0, _ := ret[0].(error)
return ret0
}
// DeleteExpense indicates an expected call of DeleteExpense.
func (mr *MockQuerierMockRecorder) DeleteExpense(ctx, id any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteExpense", reflect.TypeOf((*MockQuerier)(nil).DeleteExpense), ctx, id)
}
// DeleteTransactionsOfExpenseID mocks base method.
func (m *MockQuerier) DeleteTransactionsOfExpenseID(ctx context.Context, expenseID int32) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DeleteTransactionsOfExpenseID", ctx, expenseID)
ret0, _ := ret[0].(error)
return ret0
}
// DeleteTransactionsOfExpenseID indicates an expected call of DeleteTransactionsOfExpenseID.
func (mr *MockQuerierMockRecorder) DeleteTransactionsOfExpenseID(ctx, expenseID any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTransactionsOfExpenseID", reflect.TypeOf((*MockQuerier)(nil).DeleteTransactionsOfExpenseID), ctx, expenseID)
}
// GetEventByID mocks base method.
func (m *MockQuerier) GetEventByID(ctx context.Context, id int32) (sqlc.GetEventByIDRow, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetEventByID", ctx, id)
ret0, _ := ret[0].(sqlc.GetEventByIDRow)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetEventByID indicates an expected call of GetEventByID.
func (mr *MockQuerierMockRecorder) GetEventByID(ctx, id any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEventByID", reflect.TypeOf((*MockQuerier)(nil).GetEventByID), ctx, id)
}
// GetExpenseByID mocks base method.
func (m *MockQuerier) GetExpenseByID(ctx context.Context, id int32) (sqlc.GetExpenseByIDRow, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetExpenseByID", ctx, id)
ret0, _ := ret[0].(sqlc.GetExpenseByIDRow)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetExpenseByID indicates an expected call of GetExpenseByID.
func (mr *MockQuerierMockRecorder) GetExpenseByID(ctx, id any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetExpenseByID", reflect.TypeOf((*MockQuerier)(nil).GetExpenseByID), ctx, id)
}
// GetUserByEmail mocks base method.
func (m *MockQuerier) GetUserByEmail(ctx context.Context, email string) (sqlc.User, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetUserByEmail", ctx, email)
ret0, _ := ret[0].(sqlc.User)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetUserByEmail indicates an expected call of GetUserByEmail.
func (mr *MockQuerierMockRecorder) GetUserByEmail(ctx, email any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserByEmail", reflect.TypeOf((*MockQuerier)(nil).GetUserByEmail), ctx, email)
}
// GetUserByID mocks base method.
func (m *MockQuerier) GetUserByID(ctx context.Context, id int32) (sqlc.User, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetUserByID", ctx, id)
ret0, _ := ret[0].(sqlc.User)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetUserByID indicates an expected call of GetUserByID.
func (mr *MockQuerierMockRecorder) GetUserByID(ctx, id any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserByID", reflect.TypeOf((*MockQuerier)(nil).GetUserByID), ctx, id)
}
// InsertEvent mocks base method.
func (m *MockQuerier) InsertEvent(ctx context.Context, arg sqlc.InsertEventParams) (sqlc.Event, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "InsertEvent", ctx, arg)
ret0, _ := ret[0].(sqlc.Event)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// InsertEvent indicates an expected call of InsertEvent.
func (mr *MockQuerierMockRecorder) InsertEvent(ctx, arg any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertEvent", reflect.TypeOf((*MockQuerier)(nil).InsertEvent), ctx, arg)
}
// InsertExpense mocks base method.
func (m *MockQuerier) InsertExpense(ctx context.Context, arg sqlc.InsertExpenseParams) (sqlc.Expense, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "InsertExpense", ctx, arg)
ret0, _ := ret[0].(sqlc.Expense)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// InsertExpense indicates an expected call of InsertExpense.
func (mr *MockQuerierMockRecorder) InsertExpense(ctx, arg any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertExpense", reflect.TypeOf((*MockQuerier)(nil).InsertExpense), ctx, arg)
}
// InsertParticipation mocks base method.
func (m *MockQuerier) InsertParticipation(ctx context.Context, arg sqlc.InsertParticipationParams) (sqlc.Participation, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "InsertParticipation", ctx, arg)
ret0, _ := ret[0].(sqlc.Participation)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// InsertParticipation indicates an expected call of InsertParticipation.
func (mr *MockQuerierMockRecorder) InsertParticipation(ctx, arg any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertParticipation", reflect.TypeOf((*MockQuerier)(nil).InsertParticipation), ctx, arg)
}
// InsertTransaction mocks base method.
func (m *MockQuerier) InsertTransaction(ctx context.Context, arg sqlc.InsertTransactionParams) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "InsertTransaction", ctx, arg)
ret0, _ := ret[0].(error)
return ret0
}
// InsertTransaction indicates an expected call of InsertTransaction.
func (mr *MockQuerierMockRecorder) InsertTransaction(ctx, arg any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertTransaction", reflect.TypeOf((*MockQuerier)(nil).InsertTransaction), ctx, arg)
}
// InsertUser mocks base method.
func (m *MockQuerier) InsertUser(ctx context.Context, arg sqlc.InsertUserParams) (sqlc.User, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "InsertUser", ctx, arg)
ret0, _ := ret[0].(sqlc.User)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// InsertUser indicates an expected call of InsertUser.
func (mr *MockQuerierMockRecorder) InsertUser(ctx, arg any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertUser", reflect.TypeOf((*MockQuerier)(nil).InsertUser), ctx, arg)
}
// ListEventsByUserID mocks base method.
func (m *MockQuerier) ListEventsByUserID(ctx context.Context, userID int32) ([]sqlc.ListEventsByUserIDRow, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ListEventsByUserID", ctx, userID)
ret0, _ := ret[0].([]sqlc.ListEventsByUserIDRow)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// ListEventsByUserID indicates an expected call of ListEventsByUserID.
func (mr *MockQuerierMockRecorder) ListEventsByUserID(ctx, userID any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListEventsByUserID", reflect.TypeOf((*MockQuerier)(nil).ListEventsByUserID), ctx, userID)
}
// ListExpensesByEventID mocks base method.
func (m *MockQuerier) ListExpensesByEventID(ctx context.Context, id int32) ([]sqlc.Expense, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ListExpensesByEventID", ctx, id)
ret0, _ := ret[0].([]sqlc.Expense)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// ListExpensesByEventID indicates an expected call of ListExpensesByEventID.
func (mr *MockQuerierMockRecorder) ListExpensesByEventID(ctx, id any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListExpensesByEventID", reflect.TypeOf((*MockQuerier)(nil).ListExpensesByEventID), ctx, id)
}
// ListExpensesByEventIDByUserID mocks base method.
func (m *MockQuerier) ListExpensesByEventIDByUserID(ctx context.Context, id int32) ([]sqlc.Expense, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ListExpensesByEventIDByUserID", ctx, id)
ret0, _ := ret[0].([]sqlc.Expense)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// ListExpensesByEventIDByUserID indicates an expected call of ListExpensesByEventIDByUserID.
func (mr *MockQuerierMockRecorder) ListExpensesByEventIDByUserID(ctx, id any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListExpensesByEventIDByUserID", reflect.TypeOf((*MockQuerier)(nil).ListExpensesByEventIDByUserID), ctx, id)
}
// UpdateEventByID mocks base method.
func (m *MockQuerier) UpdateEventByID(ctx context.Context, arg sqlc.UpdateEventByIDParams) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpdateEventByID", ctx, arg)
ret0, _ := ret[0].(error)
return ret0
}
// UpdateEventByID indicates an expected call of UpdateEventByID.
func (mr *MockQuerierMockRecorder) UpdateEventByID(ctx, arg any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateEventByID", reflect.TypeOf((*MockQuerier)(nil).UpdateEventByID), ctx, arg)
}
// UpdateExpenseByID mocks base method.
func (m *MockQuerier) UpdateExpenseByID(ctx context.Context, arg sqlc.UpdateExpenseByIDParams) (sqlc.Expense, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpdateExpenseByID", ctx, arg)
ret0, _ := ret[0].(sqlc.Expense)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// UpdateExpenseByID indicates an expected call of UpdateExpenseByID.
func (mr *MockQuerierMockRecorder) UpdateExpenseByID(ctx, arg any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateExpenseByID", reflect.TypeOf((*MockQuerier)(nil).UpdateExpenseByID), ctx, arg)
}

View File

@ -19,27 +19,6 @@ type Querier interface {
InsertExpense(ctx context.Context, arg InsertExpenseParams) (Expense, error) InsertExpense(ctx context.Context, arg InsertExpenseParams) (Expense, error)
InsertParticipation(ctx context.Context, arg InsertParticipationParams) (Participation, error) InsertParticipation(ctx context.Context, arg InsertParticipationParams) (Participation, error)
InsertTransaction(ctx context.Context, arg InsertTransactionParams) error InsertTransaction(ctx context.Context, arg InsertTransactionParams) error
// 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.
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)

View File

@ -1,25 +1,3 @@
-- 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.
-- name: InsertUser :one -- name: InsertUser :one
INSERT INTO "user" ( INSERT INTO "user" (
email, first_name, last_name, password, created_at, updated_at email, first_name, last_name, password, created_at, updated_at

View File

@ -53,7 +53,6 @@ func (q *Queries) GetUserByID(ctx context.Context, id int32) (User, error) {
} }
const insertUser = `-- name: InsertUser :one const insertUser = `-- name: InsertUser :one
INSERT INTO "user" ( INSERT INTO "user" (
email, first_name, last_name, password, created_at, updated_at email, first_name, last_name, password, created_at, updated_at
) VALUES ( $1, $2, $3, $4, $5, $6 ) ) VALUES ( $1, $2, $3, $4, $5, $6 )
@ -69,27 +68,6 @@ type InsertUserParams struct {
UpdatedAt time.Time UpdatedAt time.Time
} }
// 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.
func (q *Queries) InsertUser(ctx context.Context, arg InsertUserParams) (User, error) { func (q *Queries) InsertUser(ctx context.Context, arg InsertUserParams) (User, error) {
row := q.db.QueryRowContext(ctx, insertUser, row := q.db.QueryRowContext(ctx, insertUser,
arg.Email, arg.Email,

View File

@ -31,18 +31,17 @@ import (
"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/model" "git.vinchent.xyz/vinchent/howmuch/internal/howmuch/model"
"git.vinchent.xyz/vinchent/howmuch/internal/howmuch/usecase/repo" "git.vinchent.xyz/vinchent/howmuch/internal/howmuch/usecase/repo"
"github.com/jackc/pgx/v5"
) )
type userRepository struct { type userRepository struct {
db *sql.DB querier sqlc.Querier
} }
const insertTimeout = 1 * time.Second const insertTimeout = 1 * time.Second
func NewUserRepository(db *sql.DB) repo.UserRepository { func NewUserRepository(db *sql.DB) repo.UserRepository {
return &userRepository{ return &userRepository{
db: db, querier: sqlc.New(db),
} }
} }
@ -66,7 +65,7 @@ func (ur *userRepository) Create(
tx, ok := transaction.(*sql.Tx) tx, ok := transaction.(*sql.Tx)
if !ok { if !ok {
return nil, errors.New("transaction is not a pgx.Tx") return nil, errors.New("transaction is not a *sql.Tx")
} }
queries := sqlc.New(tx) queries := sqlc.New(tx)
@ -89,9 +88,8 @@ func (ur *userRepository) Create(
// GetByEmail if not found, return nil for user but not error. // GetByEmail if not found, return nil for user but not error.
func (ur *userRepository) GetByEmail(ctx context.Context, email string) (*model.UserEntity, error) { func (ur *userRepository) GetByEmail(ctx context.Context, email string) (*model.UserEntity, error) {
queries := sqlc.New(ur.db) userDB, err := ur.querier.GetUserByEmail(ctx, email)
userDB, err := queries.GetUserByEmail(ctx, email) if errors.Is(err, sql.ErrNoRows) {
if errors.Is(err, pgx.ErrNoRows) {
// No query error, but user not found // No query error, but user not found
return nil, nil return nil, nil
} else if err != nil { } else if err != nil {
@ -110,9 +108,8 @@ func (ur *userRepository) GetByEmail(ctx context.Context, email string) (*model.
} }
func (ur *userRepository) GetByID(ctx context.Context, id int) (*model.UserEntity, error) { func (ur *userRepository) GetByID(ctx context.Context, id int) (*model.UserEntity, error) {
queries := sqlc.New(ur.db) userDB, err := ur.querier.GetUserByID(ctx, int32(id))
userDB, err := queries.GetUserByID(ctx, int32(id)) if errors.Is(err, sql.ErrNoRows) {
if errors.Is(err, pgx.ErrNoRows) {
// No query error, but user not found // No query error, but user not found
return nil, nil return nil, nil
} else if err != nil { } else if err != nil {

View File

@ -63,14 +63,6 @@ type EventInfoResponse struct {
// }}} // }}}
// {{{ Entity (DB In) // {{{ Entity (DB In)
type EventBaseItemEntity struct {
ID int
Name string
Description string
OwnerID int
CreatedAt time.Time
}
type EventEntity struct { type EventEntity struct {
ID int ID int
@ -84,6 +76,14 @@ type EventEntity struct {
UpdatedAt time.Time UpdatedAt time.Time
} }
type EventUpdateEntity struct {
ID int
Name string
Description string
CreatedAt time.Time
// TODO: maybe I can change owner too
}
// }}} // }}}
// {{{ Retrieved (DB out) // {{{ Retrieved (DB out)
@ -103,6 +103,14 @@ type EventRetrieved struct {
UpdatedAt time.Time UpdatedAt time.Time
} }
type EventListRetrieved struct {
ID int
Name string
Description string
CreatedAt time.Time
Owner *UserBaseRetrieved
}
// }}} // }}}
// {{{ DO Domain Object (Contains the domain service) // {{{ DO Domain Object (Contains the domain service)

View File

@ -22,7 +22,9 @@
package repo package repo
import "context" import (
"context"
)
type DBRepository interface { type DBRepository interface {
Transaction( Transaction(

View File

@ -31,17 +31,13 @@ import (
type EventRepository interface { type EventRepository interface {
Create(ctx context.Context, evEntity *model.EventEntity) (*model.EventEntity, error) Create(ctx context.Context, evEntity *model.EventEntity) (*model.EventEntity, error)
// UpdateInfo updates the event related information (name, descriptions) // UpdateEventByID updates the event related information (name, descriptions)
UpdateInfo() UpdateEventByID(ctx context.Context, event *model.EventUpdateEntity) error
Delete() // XXX: Pay attention to the foreign key relationships
GetByID(ctx context.Context, eventID int) (*model.EventRetrieved, error) GetByID(ctx context.Context, eventID int) (*model.EventRetrieved, error)
ListExpensesByUserID()
// related to events of a user // related to events of a user
ListEventsByUserID(ctx context.Context, userID int) ([]model.EventBaseItemEntity, error) ListEventsByUserID(ctx context.Context, userID int) ([]*model.EventListRetrieved, error)
} }
type ExpenseRepository interface { type ExpenseRepository interface {
@ -49,6 +45,7 @@ type ExpenseRepository interface {
Update() Update()
Delete() // Delete also the related transactions Delete() // Delete also the related transactions
ListExpensesByUserID()
GetByID() GetByID()
} }

View File

@ -29,7 +29,11 @@ import (
) )
type UserRepository interface { type UserRepository interface {
Create(ctx context.Context, transaction interface{}, u *model.UserEntity) (*model.UserEntity, error) Create(
ctx context.Context,
transaction interface{},
u *model.UserEntity,
) (*model.UserEntity, error)
GetByEmail(ctx context.Context, email string) (*model.UserEntity, error) GetByEmail(ctx context.Context, email string) (*model.UserEntity, error)
GetByID(ctx context.Context, id int) (*model.UserEntity, error) GetByID(ctx context.Context, id int) (*model.UserEntity, error)
} }

View File

@ -22,7 +22,9 @@
package repomock package repomock
import "context" import (
"context"
)
type TestDBRepository struct{} type TestDBRepository struct{}