Skip to content

Commit

Permalink
feat: [#358] Add Update Delete methods for DB
Browse files Browse the repository at this point in the history
  • Loading branch information
hwbrzzl committed Feb 21, 2025
1 parent 92e3b3e commit c45f273
Show file tree
Hide file tree
Showing 6 changed files with 292 additions and 11 deletions.
2 changes: 2 additions & 0 deletions contracts/database/db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ type DB interface {

type Query interface {
First(dest any) error
Delete() (*Result, error)
Get(dest any) error
Insert(data any) (*Result, error)
Update(data any) (*Result, error)
Where(query any, args ...any) Query
}

Expand Down
113 changes: 111 additions & 2 deletions database/db/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,29 @@ func NewQuery(config database.Config, builder db.Builder, table string) *Query {
}
}

func (r *Query) Delete() (*db.Result, error) {
sql, args, err := r.buildDelete()
// TODO: use logger instead of println
fmt.Println(sql, args, err)
if err != nil {
return nil, err
}

result, err := r.builder.Exec(sql, args...)
if err != nil {
return nil, err
}

rowsAffected, err := result.RowsAffected()
if err != nil {
return nil, err
}

return &db.Result{
RowsAffected: rowsAffected,
}, nil
}

func (r *Query) First(dest any) error {
sql, args, err := r.buildSelect()
// TODO: use logger instead of println
Expand Down Expand Up @@ -82,6 +105,34 @@ func (r *Query) Insert(data any) (*db.Result, error) {
}, nil
}

func (r *Query) Update(data any) (*db.Result, error) {
mapData, err := convertToMap(data)
if err != nil {
return nil, err
}

sql, args, err := r.buildUpdate(mapData)
// TODO: use logger instead of println
fmt.Println(sql, args, err)
if err != nil {
return nil, err
}

result, err := r.builder.Exec(sql, args...)
if err != nil {
return nil, err
}

rowsAffected, err := result.RowsAffected()
if err != nil {
return nil, err
}

return &db.Result{
RowsAffected: rowsAffected,
}, nil
}

func (r *Query) Where(query any, args ...any) db.Query {
q := NewQuery(r.config, r.builder, r.conditions.table)
q.conditions = r.conditions
Expand All @@ -93,6 +144,35 @@ func (r *Query) Where(query any, args ...any) db.Query {
return q
}

func (r *Query) buildDelete() (sql string, args []any, err error) {
if r.conditions.table == "" {
return "", nil, errors.DatabaseTableIsRequired
}

builder := sq.Delete(r.conditions.table)
if r.config.PlaceholderFormat != nil {
builder = builder.PlaceholderFormat(r.config.PlaceholderFormat)
}

for _, where := range r.conditions.where {
query, ok := where.query.(string)
if ok {
if !str.Of(query).Trim().Contains(" ", "?") {
if len(where.args) > 1 {
builder = builder.Where(sq.Eq{query: where.args})
} else if len(where.args) == 1 {
builder = builder.Where(sq.Eq{query: where.args[0]})
}
continue
}
}

builder = builder.Where(where.query, where.args...)
}

return builder.ToSql()
}

func (r *Query) buildInsert(data []map[string]any) (sql string, args []any, err error) {
if r.conditions.table == "" {
return "", nil, errors.DatabaseTableIsRequired
Expand All @@ -104,8 +184,6 @@ func (r *Query) buildInsert(data []map[string]any) (sql string, args []any, err
}

first := data[0]
builder = builder.SetMap(first)

cols := make([]string, 0, len(first))
for col := range first {
cols = append(cols, col)
Expand Down Expand Up @@ -154,3 +232,34 @@ func (r *Query) buildSelect() (sql string, args []any, err error) {

return builder.ToSql()
}

func (r *Query) buildUpdate(data map[string]any) (sql string, args []any, err error) {
if r.conditions.table == "" {
return "", nil, errors.DatabaseTableIsRequired
}

builder := sq.Update(r.conditions.table)
if r.config.PlaceholderFormat != nil {
builder = builder.PlaceholderFormat(r.config.PlaceholderFormat)
}

for _, where := range r.conditions.where {
query, ok := where.query.(string)
if ok {
if !str.Of(query).Trim().Contains(" ", "?") {
if len(where.args) > 1 {
builder = builder.Where(sq.Eq{query: where.args})
} else if len(where.args) == 1 {
builder = builder.Where(sq.Eq{query: where.args[0]})
}
continue
}
}

builder = builder.Where(where.query, where.args...)
}

builder = builder.SetMap(data)

return builder.ToSql()
}
57 changes: 54 additions & 3 deletions database/db/query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ import (

// TestUser is a test model
type TestUser struct {
ID uint `db:"id"`
Name string `db:"-"`
Age int
ID uint `db:"id"`
Phone string `db:"phone"`
Name string `db:"-"`
Age int
}

type QueryTestSuite struct {
Expand All @@ -34,6 +35,18 @@ func (s *QueryTestSuite) SetupTest() {
s.query = NewQuery(database.Config{}, s.mockBuilder, "users")
}

func (s *QueryTestSuite) TestDelete() {
mockResult := &MockResult{}
mockResult.On("RowsAffected").Return(int64(1), nil)
s.mockBuilder.EXPECT().Exec("DELETE FROM users WHERE name = ? AND id = ?", "John", 1).Return(mockResult, nil).Once()

result, err := s.query.Where("name", "John").Where("id", 1).Delete()
s.Nil(err)
s.Equal(int64(1), result.RowsAffected)

mockResult.AssertExpectations(s.T())
}

func (s *QueryTestSuite) TestFirst() {
var user TestUser
s.mockBuilder.EXPECT().Get(&user, "SELECT * FROM users WHERE name = ?", "John").Return(nil).Once()
Expand Down Expand Up @@ -150,6 +163,44 @@ func (s *QueryTestSuite) TestInsert() {
})
}

func (s *QueryTestSuite) TestUpdate() {
s.Run("single struct", func() {
user := TestUser{
Phone: "1234567890",
Name: "John",
Age: 25,
}

mockResult := &MockResult{}
mockResult.On("RowsAffected").Return(int64(1), nil)
s.mockBuilder.EXPECT().Exec("UPDATE users SET phone = ? WHERE name = ? AND id = ?", "1234567890", "John", 1).Return(mockResult, nil).Once()

result, err := s.query.Where("name", "John").Where("id", 1).Update(user)
s.Nil(err)
s.Equal(int64(1), result.RowsAffected)

mockResult.AssertExpectations(s.T())
})

s.Run("single map", func() {
user := map[string]any{
"phone": "1234567890",
"name": "John",
"age": 25,
}

mockResult := &MockResult{}
mockResult.On("RowsAffected").Return(int64(1), nil)
s.mockBuilder.EXPECT().Exec("UPDATE users SET age = ?, name = ?, phone = ? WHERE name = ? AND id = ?", 25, "John", "1234567890", "John", 1).Return(mockResult, nil).Once()

result, err := s.query.Where("name", "John").Where("id", 1).Update(user)
s.Nil(err)
s.Equal(int64(1), result.RowsAffected)

mockResult.AssertExpectations(s.T())
})
}

func (s *QueryTestSuite) TestWhere() {
s.Run("simple where condition", func() {
var user TestUser
Expand Down
3 changes: 3 additions & 0 deletions database/db/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ func convertToMap(data any) (map[string]any, error) {
if fieldValue.Kind() == reflect.Ptr && !fieldValue.IsNil() {
fieldValue = fieldValue.Elem()
}
if fieldValue.IsZero() {
continue
}
result[fieldName] = fieldValue.Interface()
}
return result, nil
Expand Down
13 changes: 7 additions & 6 deletions database/db/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
)

type Body struct {
Length int `db:"length"`
Weight string `db:"weight"`
Height int `db:"-"`
Age uint
Expand All @@ -31,29 +32,29 @@ func TestConvertToSliceMap(t *testing.T) {
{
data: []User{
{ID: 1, Name: "John", Email: "[email protected]", Body: Body{Weight: "100kg", Height: 180, Age: 25}},
{ID: 2, Name: "Jane", Email: "[email protected]", Body: Body{Weight: "90kg", Height: 170, Age: 20}},
{ID: 2, Name: "Jane", Email: "[email protected]", Body: Body{Length: 1, Weight: "90kg", Height: 170, Age: 20}},
},
want: []map[string]any{
{"id": 1, "weight": "100kg"},
{"id": 2, "weight": "90kg"},
{"id": 2, "length": 1, "weight": "90kg"},
},
},
{
data: []*User{
{ID: 1, Name: "John", Email: "[email protected]", Body: Body{Weight: "100kg", Height: 180, Age: 25}},
{ID: 2, Name: "Jane", Email: "[email protected]", Body: Body{Weight: "90kg", Height: 170, Age: 20}},
{ID: 2, Name: "Jane", Email: "[email protected]", Body: Body{Length: 1, Weight: "90kg", Height: 170, Age: 20}},
},
want: []map[string]any{
{"id": 1, "weight": "100kg"},
{"id": 2, "weight": "90kg"},
{"id": 2, "length": 1, "weight": "90kg"},
},
},
{
data: []Body{
{Weight: "100kg", Height: 180, Age: 25},
{Weight: "90kg", Height: 170, Age: 20},
{Length: 1, Weight: "90kg", Height: 170, Age: 20},
},
want: []map[string]any{{"weight": "100kg"}, {"weight": "90kg"}},
want: []map[string]any{{"weight": "100kg"}, {"length": 1, "weight": "90kg"}},
},
{
data: Body{
Expand Down
Loading

0 comments on commit c45f273

Please sign in to comment.