Unistore Chore: Make it easier to implement DB interface (#94680)

make it easier to implement DB interface
This commit is contained in:
Diego Augusto Molina
2024-10-16 13:40:35 -03:00
committed by GitHub
parent bd321216db
commit 44282134da
4 changed files with 185 additions and 180 deletions
+67
View File
@@ -3,6 +3,7 @@ package db
import (
"context"
"database/sql"
"fmt"
)
//go:generate mockery --with-expecter --name DB
@@ -62,3 +63,69 @@ type ContextExecer interface {
QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row
}
// WithTxFunc is an adapter to be able to provide the DB.WithTx method as an
// embedded function.
type WithTxFunc func(context.Context, *sql.TxOptions, TxFunc) error
// WithTx implements the DB.WithTx method.
func (x WithTxFunc) WithTx(ctx context.Context, opts *sql.TxOptions, f TxFunc) error {
return x(ctx, opts, f)
}
// BeginTxFunc is the signature of the DB.BeginTx method.
type BeginTxFunc = func(context.Context, *sql.TxOptions) (Tx, error)
// NewWithTxFunc provides implementations of DB an easy way to provide the
// DB.WithTx method.
// Example usage:
//
// type myDB struct {
// db.WithTxFunc // embedded so that `WithTx` is already provided
// // other members...
// }
//
// func NewMyDB(/* options */) (db.DB, error) {
// ret := new(myDB)
// ret.WithTxFunc = db.NewWithTxFunc(ret.BeginTx)
// // other initialization code ...
// return ret, nil
// }
func NewWithTxFunc(x BeginTxFunc) WithTxFunc {
return WithTxFunc(
func(ctx context.Context, opts *sql.TxOptions, f TxFunc) error {
t, err := x(ctx, opts)
if err != nil {
return fmt.Errorf(oneErrFmt, beginStr, err)
}
if err := f(ctx, t); err != nil {
if rollbackErr := t.Rollback(); rollbackErr != nil {
return fmt.Errorf(twoErrFmt, txOpStr, err, rollbackStr,
rollbackErr)
}
return fmt.Errorf(oneErrFmt, txOpStr, err)
}
if err = t.Commit(); err != nil {
return fmt.Errorf(oneErrFmt, commitStr, err)
}
return nil
},
)
}
// Constants that allow testing that the correct scenario was hit.
const (
oneErrFmt = "%s: %w"
twoErrFmt = oneErrFmt + "; " + oneErrFmt
// keep the following ones in sync with the matching ones in
// `service_test.go`.
txOpStr = "transactional operation"
beginStr = "begin"
commitStr = "commit"
rollbackStr = "rollback"
)