Improve the names of some public types *breaking change*

pull/2/head
Vinícius Garcia 2021-06-28 17:35:46 -03:00
parent 682f99b495
commit b6e6667a3f
9 changed files with 92 additions and 77 deletions

View File

@ -68,8 +68,8 @@ The current interface is as follows and we plan on keeping
it with as little functions as possible, so don't expect many additions:
```go
// SQLProvider describes the public behavior of this ORM
type SQLProvider interface {
// Provider describes the public behavior of this ORM
type Provider interface {
Insert(ctx context.Context, table Table, record interface{}) error
Update(ctx context.Context, table Table, record interface{}) error
Delete(ctx context.Context, table Table, idsOrRecords ...interface{}) error
@ -79,7 +79,7 @@ type SQLProvider interface {
QueryChunks(ctx context.Context, parser ChunkParser) error
Exec(ctx context.Context, query string, params ...interface{}) error
Transaction(ctx context.Context, fn func(SQLProvider) error) error
Transaction(ctx context.Context, fn func(Provider) error) error
}
```
@ -232,7 +232,7 @@ func main() {
}
// Making transactions:
err = db.Transaction(ctx, func(db ksql.SQLProvider) error {
err = db.Transaction(ctx, func(db ksql.Provider) error {
var cris2 User
err = db.QueryOne(ctx, &cris2, "SELECT * FROM users WHERE id = ?", cris.ID)
if err != nil {
@ -372,10 +372,10 @@ Querying a single joined row:
```golang
var row struct{
User User `tablename:"u"` // (here the tablename must match the aliased tablename in the query)
Post Post `tablename:"p"` // (if no alias is used you should use the actual name of the table)
User User `tablename:"u"` // (here the tablename must match the aliased tablename in the query)
Post Post `tablename:"posts"` // (if no alias is used you should use the actual name of the table)
}
err = db.QueryOne(ctx, &row, "FROM users as u JOIN posts as p ON u.id = p.user_id WHERE u.id = ?", userID)
err = db.QueryOne(ctx, &row, "FROM users as u JOIN posts ON u.id = posts.user_id WHERE u.id = ?", userID)
if err != nil {
panic(err.Error())
}
@ -521,13 +521,13 @@ make test
### TODO List
- Improve error messages
- Add tests for tables using composite keys
- Add support for serializing structs as other formats such as YAML
- Update `kstructs.FillStructWith` to work with `json` tagged attributes
- Make testing easier by exposing the connection strings in an .env file
- Make testing easier by automatically creating the `ksql` database
- Create a way for users to submit user defined dialects
- Improve error messages
### Optimization Oportunities

View File

@ -14,8 +14,8 @@ var ErrRecordNotFound error = errors.Wrap(sql.ErrNoRows, "ksql: the query return
// ErrAbortIteration ...
var ErrAbortIteration error = fmt.Errorf("ksql: abort iteration, should only be used inside QueryChunks function")
// SQLProvider describes the public behavior of this ORM
type SQLProvider interface {
// Provider describes the public behavior of this ORM
type Provider interface {
Insert(ctx context.Context, table Table, record interface{}) error
Update(ctx context.Context, table Table, record interface{}) error
Delete(ctx context.Context, table Table, idsOrRecords ...interface{}) error
@ -25,7 +25,7 @@ type SQLProvider interface {
QueryChunks(ctx context.Context, parser ChunkParser) error
Exec(ctx context.Context, query string, params ...interface{}) error
Transaction(ctx context.Context, fn func(SQLProvider) error) error
Transaction(ctx context.Context, fn func(Provider) error) error
}
// Table describes the required information for inserting, updating and

View File

@ -137,7 +137,7 @@ func main() {
}
// Making transactions:
err = db.Transaction(ctx, func(db ksql.SQLProvider) error {
err = db.Transaction(ctx, func(db ksql.Provider) error {
var cris2 User
err = db.QueryOne(ctx, &cris2, "SELECT * FROM users WHERE id = ?", cris.ID)
if err != nil {

View File

@ -40,12 +40,12 @@ type Address struct {
// Service ...
type Service struct {
db ksql.SQLProvider
db ksql.Provider
streamChunkSize int
}
// NewUserService ...
func NewUserService(db ksql.SQLProvider) Service {
func NewUserService(db ksql.Provider) Service {
return Service{
db: db,
streamChunkSize: 100,

View File

@ -7,8 +7,8 @@ import (
gomock "github.com/golang/mock/gomock"
"github.com/tj/assert"
"github.com/vingarcia/ksql"
"github.com/vingarcia/ksql/nullable"
"github.com/vingarcia/ksql/kstructs"
"github.com/vingarcia/ksql/nullable"
)
func TestCreateUser(t *testing.T) {
@ -16,7 +16,7 @@ func TestCreateUser(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
mockDB := NewMockSQLProvider(controller)
mockDB := NewMockProvider(controller)
s := Service{
db: mockDB,
@ -42,7 +42,7 @@ func TestCreateUser(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
mockDB := NewMockSQLProvider(controller)
mockDB := NewMockProvider(controller)
s := Service{
db: mockDB,
@ -82,7 +82,7 @@ func TestUpdateUserScore(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
mockDB := NewMockSQLProvider(controller)
mockDB := NewMockProvider(controller)
s := Service{
db: mockDB,
@ -126,7 +126,7 @@ func TestListUsers(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
mockDB := NewMockSQLProvider(controller)
mockDB := NewMockProvider(controller)
s := Service{
db: mockDB,
@ -188,7 +188,7 @@ func TestStreamAllUsers(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
mockDB := NewMockSQLProvider(controller)
mockDB := NewMockProvider(controller)
s := Service{
db: mockDB,
@ -260,7 +260,7 @@ func TestDeleteUser(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
mockDB := NewMockSQLProvider(controller)
mockDB := NewMockProvider(controller)
s := Service{
db: mockDB,

View File

@ -12,31 +12,31 @@ import (
ksql "github.com/vingarcia/ksql"
)
// MockSQLProvider is a mock of SQLProvider interface.
type MockSQLProvider struct {
// MockProvider is a mock of Provider interface.
type MockProvider struct {
ctrl *gomock.Controller
recorder *MockSQLProviderMockRecorder
recorder *MockProviderMockRecorder
}
// MockSQLProviderMockRecorder is the mock recorder for MockSQLProvider.
type MockSQLProviderMockRecorder struct {
mock *MockSQLProvider
// MockProviderMockRecorder is the mock recorder for MockProvider.
type MockProviderMockRecorder struct {
mock *MockProvider
}
// NewMockSQLProvider creates a new mock instance.
func NewMockSQLProvider(ctrl *gomock.Controller) *MockSQLProvider {
mock := &MockSQLProvider{ctrl: ctrl}
mock.recorder = &MockSQLProviderMockRecorder{mock}
// NewMockProvider creates a new mock instance.
func NewMockProvider(ctrl *gomock.Controller) *MockProvider {
mock := &MockProvider{ctrl: ctrl}
mock.recorder = &MockProviderMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockSQLProvider) EXPECT() *MockSQLProviderMockRecorder {
func (m *MockProvider) EXPECT() *MockProviderMockRecorder {
return m.recorder
}
// Delete mocks base method.
func (m *MockSQLProvider) Delete(ctx context.Context, table ksql.Table, idsOrRecords ...interface{}) error {
func (m *MockProvider) Delete(ctx context.Context, table ksql.Table, idsOrRecords ...interface{}) error {
m.ctrl.T.Helper()
varargs := []interface{}{ctx, table}
for _, a := range idsOrRecords {
@ -48,14 +48,14 @@ func (m *MockSQLProvider) Delete(ctx context.Context, table ksql.Table, idsOrRec
}
// Delete indicates an expected call of Delete.
func (mr *MockSQLProviderMockRecorder) Delete(ctx, table interface{}, idsOrRecords ...interface{}) *gomock.Call {
func (mr *MockProviderMockRecorder) Delete(ctx, table interface{}, idsOrRecords ...interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{ctx, table}, idsOrRecords...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockSQLProvider)(nil).Delete), varargs...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockProvider)(nil).Delete), varargs...)
}
// Exec mocks base method.
func (m *MockSQLProvider) Exec(ctx context.Context, query string, params ...interface{}) error {
func (m *MockProvider) Exec(ctx context.Context, query string, params ...interface{}) error {
m.ctrl.T.Helper()
varargs := []interface{}{ctx, query}
for _, a := range params {
@ -67,14 +67,14 @@ func (m *MockSQLProvider) Exec(ctx context.Context, query string, params ...inte
}
// Exec indicates an expected call of Exec.
func (mr *MockSQLProviderMockRecorder) Exec(ctx, query interface{}, params ...interface{}) *gomock.Call {
func (mr *MockProviderMockRecorder) Exec(ctx, query interface{}, params ...interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{ctx, query}, params...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Exec", reflect.TypeOf((*MockSQLProvider)(nil).Exec), varargs...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Exec", reflect.TypeOf((*MockProvider)(nil).Exec), varargs...)
}
// Insert mocks base method.
func (m *MockSQLProvider) Insert(ctx context.Context, table ksql.Table, record interface{}) error {
func (m *MockProvider) Insert(ctx context.Context, table ksql.Table, record interface{}) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Insert", ctx, table, record)
ret0, _ := ret[0].(error)
@ -82,13 +82,13 @@ func (m *MockSQLProvider) Insert(ctx context.Context, table ksql.Table, record i
}
// Insert indicates an expected call of Insert.
func (mr *MockSQLProviderMockRecorder) Insert(ctx, table, record interface{}) *gomock.Call {
func (mr *MockProviderMockRecorder) Insert(ctx, table, record interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockSQLProvider)(nil).Insert), ctx, table, record)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockProvider)(nil).Insert), ctx, table, record)
}
// Query mocks base method.
func (m *MockSQLProvider) Query(ctx context.Context, records interface{}, query string, params ...interface{}) error {
func (m *MockProvider) Query(ctx context.Context, records interface{}, query string, params ...interface{}) error {
m.ctrl.T.Helper()
varargs := []interface{}{ctx, records, query}
for _, a := range params {
@ -100,14 +100,14 @@ func (m *MockSQLProvider) Query(ctx context.Context, records interface{}, query
}
// Query indicates an expected call of Query.
func (mr *MockSQLProviderMockRecorder) Query(ctx, records, query interface{}, params ...interface{}) *gomock.Call {
func (mr *MockProviderMockRecorder) Query(ctx, records, query interface{}, params ...interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{ctx, records, query}, params...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Query", reflect.TypeOf((*MockSQLProvider)(nil).Query), varargs...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Query", reflect.TypeOf((*MockProvider)(nil).Query), varargs...)
}
// QueryChunks mocks base method.
func (m *MockSQLProvider) QueryChunks(ctx context.Context, parser ksql.ChunkParser) error {
func (m *MockProvider) QueryChunks(ctx context.Context, parser ksql.ChunkParser) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "QueryChunks", ctx, parser)
ret0, _ := ret[0].(error)
@ -115,13 +115,13 @@ func (m *MockSQLProvider) QueryChunks(ctx context.Context, parser ksql.ChunkPars
}
// QueryChunks indicates an expected call of QueryChunks.
func (mr *MockSQLProviderMockRecorder) QueryChunks(ctx, parser interface{}) *gomock.Call {
func (mr *MockProviderMockRecorder) QueryChunks(ctx, parser interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryChunks", reflect.TypeOf((*MockSQLProvider)(nil).QueryChunks), ctx, parser)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryChunks", reflect.TypeOf((*MockProvider)(nil).QueryChunks), ctx, parser)
}
// QueryOne mocks base method.
func (m *MockSQLProvider) QueryOne(ctx context.Context, record interface{}, query string, params ...interface{}) error {
func (m *MockProvider) QueryOne(ctx context.Context, record interface{}, query string, params ...interface{}) error {
m.ctrl.T.Helper()
varargs := []interface{}{ctx, record, query}
for _, a := range params {
@ -133,14 +133,14 @@ func (m *MockSQLProvider) QueryOne(ctx context.Context, record interface{}, quer
}
// QueryOne indicates an expected call of QueryOne.
func (mr *MockSQLProviderMockRecorder) QueryOne(ctx, record, query interface{}, params ...interface{}) *gomock.Call {
func (mr *MockProviderMockRecorder) QueryOne(ctx, record, query interface{}, params ...interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{ctx, record, query}, params...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryOne", reflect.TypeOf((*MockSQLProvider)(nil).QueryOne), varargs...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryOne", reflect.TypeOf((*MockProvider)(nil).QueryOne), varargs...)
}
// Transaction mocks base method.
func (m *MockSQLProvider) Transaction(ctx context.Context, fn func(ksql.SQLProvider) error) error {
func (m *MockProvider) Transaction(ctx context.Context, fn func(ksql.Provider) error) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Transaction", ctx, fn)
ret0, _ := ret[0].(error)
@ -148,13 +148,13 @@ func (m *MockSQLProvider) Transaction(ctx context.Context, fn func(ksql.SQLProvi
}
// Transaction indicates an expected call of Transaction.
func (mr *MockSQLProviderMockRecorder) Transaction(ctx, fn interface{}) *gomock.Call {
func (mr *MockProviderMockRecorder) Transaction(ctx, fn interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Transaction", reflect.TypeOf((*MockSQLProvider)(nil).Transaction), ctx, fn)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Transaction", reflect.TypeOf((*MockProvider)(nil).Transaction), ctx, fn)
}
// Update mocks base method.
func (m *MockSQLProvider) Update(ctx context.Context, table ksql.Table, record interface{}) error {
func (m *MockProvider) Update(ctx context.Context, table ksql.Table, record interface{}) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Update", ctx, table, record)
ret0, _ := ret[0].(error)
@ -162,7 +162,7 @@ func (m *MockSQLProvider) Update(ctx context.Context, table ksql.Table, record i
}
// Update indicates an expected call of Update.
func (mr *MockSQLProviderMockRecorder) Update(ctx, table, record interface{}) *gomock.Call {
func (mr *MockProviderMockRecorder) Update(ctx, table, record interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockSQLProvider)(nil).Update), ctx, table, record)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockProvider)(nil).Update), ctx, table, record)
}

23
ksql.go
View File

@ -22,14 +22,19 @@ func init() {
// DB represents the ksql client responsible for
// interfacing with the "database/sql" package implementing
// the KissSQL interface `SQLProvider`.
// the KissSQL interface `ksql.Provider`.
type DB struct {
driver string
dialect dialect
db sqlProvider
db DBAdapter
}
type sqlProvider interface {
// DBAdapter is minimalistic interface to decouple our implementation
// from database/sql, i.e. if any struct implements the functions below
// with the exact same semantic as the sql package it will work with ksql.
//
// To create a new client using this adapter use ksql.NewWithDB()
type DBAdapter interface {
ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)
QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)
}
@ -61,6 +66,16 @@ func New(
db.SetMaxOpenConns(config.MaxOpenConns)
return newWithDB(db, dbDriver, connectionString)
}
// NewWithDB allows the user to insert a custom implementation
// of the DBAdapter interface
func newWithDB(
db DBAdapter,
dbDriver string,
connectionString string,
) (DB, error) {
dialect := supportedDialects[dbDriver]
if dialect == nil {
return DB{}, fmt.Errorf("unsupported driver `%s`", dbDriver)
@ -731,7 +746,7 @@ func (c DB) Exec(ctx context.Context, query string, params ...interface{}) error
}
// Transaction just runs an SQL command on the database returning no rows.
func (c DB) Transaction(ctx context.Context, fn func(SQLProvider) error) error {
func (c DB) Transaction(ctx context.Context, fn func(Provider) error) error {
switch db := c.db.(type) {
case *sql.Tx:
return fn(c)

View File

@ -1588,7 +1588,7 @@ func TestTransaction(t *testing.T) {
_ = c.Insert(ctx, UsersTable, &User{Name: "User2"})
var users []User
err = c.Transaction(ctx, func(db SQLProvider) error {
err = c.Transaction(ctx, func(db Provider) error {
db.Query(ctx, &users, "SELECT * FROM users ORDER BY id ASC")
return nil
})
@ -1616,7 +1616,7 @@ func TestTransaction(t *testing.T) {
_ = c.Insert(ctx, UsersTable, &u1)
_ = c.Insert(ctx, UsersTable, &u2)
err = c.Transaction(ctx, func(db SQLProvider) error {
err = c.Transaction(ctx, func(db Provider) error {
err = db.Insert(ctx, UsersTable, &User{Name: "User3"})
assert.Equal(t, nil, err)
err = db.Insert(ctx, UsersTable, &User{Name: "User4"})
@ -1878,7 +1878,7 @@ func shiftErrSlice(errs *[]error) error {
return err
}
func getUsersByID(dbi sqlProvider, dialect dialect, resultsPtr *[]User, ids ...uint) error {
func getUsersByID(dbi DBAdapter, dialect dialect, resultsPtr *[]User, ids ...uint) error {
db := dbi.(*sql.DB)
placeholders := make([]string, len(ids))
@ -1920,7 +1920,7 @@ func getUsersByID(dbi sqlProvider, dialect dialect, resultsPtr *[]User, ids ...u
return nil
}
func getUserByID(dbi sqlProvider, dialect dialect, result *User, id uint) error {
func getUserByID(dbi DBAdapter, dialect dialect, result *User, id uint) error {
db := dbi.(*sql.DB)
row := db.QueryRow(`SELECT id, name, age, address FROM users WHERE id=`+dialect.Placeholder(0), id)
@ -1941,7 +1941,7 @@ func getUserByID(dbi sqlProvider, dialect dialect, result *User, id uint) error
return nil
}
func getUserByName(dbi sqlProvider, dialect dialect, result *User, name string) error {
func getUserByName(dbi DBAdapter, dialect dialect, result *User, name string) error {
db := dbi.(*sql.DB)
row := db.QueryRow(`SELECT id, name, age, address FROM users WHERE name=`+dialect.Placeholder(0), name)

View File

@ -2,10 +2,10 @@ package ksql
import "context"
var _ SQLProvider = MockSQLProvider{}
var _ Provider = Mock{}
// MockSQLProvider ...
type MockSQLProvider struct {
// Mock ...
type Mock struct {
InsertFn func(ctx context.Context, table Table, record interface{}) error
UpdateFn func(ctx context.Context, table Table, record interface{}) error
DeleteFn func(ctx context.Context, table Table, ids ...interface{}) error
@ -15,45 +15,45 @@ type MockSQLProvider struct {
QueryChunksFn func(ctx context.Context, parser ChunkParser) error
ExecFn func(ctx context.Context, query string, params ...interface{}) error
TransactionFn func(ctx context.Context, fn func(db SQLProvider) error) error
TransactionFn func(ctx context.Context, fn func(db Provider) error) error
}
// Insert ...
func (m MockSQLProvider) Insert(ctx context.Context, table Table, record interface{}) error {
func (m Mock) Insert(ctx context.Context, table Table, record interface{}) error {
return m.InsertFn(ctx, table, record)
}
// Update ...
func (m MockSQLProvider) Update(ctx context.Context, table Table, record interface{}) error {
func (m Mock) Update(ctx context.Context, table Table, record interface{}) error {
return m.UpdateFn(ctx, table, record)
}
// Delete ...
func (m MockSQLProvider) Delete(ctx context.Context, table Table, ids ...interface{}) error {
func (m Mock) Delete(ctx context.Context, table Table, ids ...interface{}) error {
return m.DeleteFn(ctx, table, ids...)
}
// Query ...
func (m MockSQLProvider) Query(ctx context.Context, records interface{}, query string, params ...interface{}) error {
func (m Mock) Query(ctx context.Context, records interface{}, query string, params ...interface{}) error {
return m.QueryFn(ctx, records, query, params...)
}
// QueryOne ...
func (m MockSQLProvider) QueryOne(ctx context.Context, record interface{}, query string, params ...interface{}) error {
func (m Mock) QueryOne(ctx context.Context, record interface{}, query string, params ...interface{}) error {
return m.QueryOneFn(ctx, record, query, params...)
}
// QueryChunks ...
func (m MockSQLProvider) QueryChunks(ctx context.Context, parser ChunkParser) error {
func (m Mock) QueryChunks(ctx context.Context, parser ChunkParser) error {
return m.QueryChunksFn(ctx, parser)
}
// Exec ...
func (m MockSQLProvider) Exec(ctx context.Context, query string, params ...interface{}) error {
func (m Mock) Exec(ctx context.Context, query string, params ...interface{}) error {
return m.ExecFn(ctx, query, params...)
}
// Transaction ...
func (m MockSQLProvider) Transaction(ctx context.Context, fn func(db SQLProvider) error) error {
func (m Mock) Transaction(ctx context.Context, fn func(db Provider) error) error {
return m.TransactionFn(ctx, fn)
}