-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'fix/new-rfq' of https://github.com/synapsecns/sanguine …
…into fix/new-rfq
- Loading branch information
Showing
12 changed files
with
373 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package db | ||
|
||
import ( | ||
"time" | ||
|
||
"github.com/shopspring/decimal" | ||
) | ||
|
||
type QuoteModel struct { | ||
// ID is the unique identifier saved of each quote provided | ||
ID uint64 `gorm:"column:id;primaryKey;"` | ||
// DestChainID is the chain which the relayer is willing to provide liquidity for | ||
DestChainID uint64 `gorm:"column:dest_chain_id;index"` | ||
// DestToken is the token address for which the relayer is providing liquidity | ||
DestTokenAddr string `gorm:"column:token;index"` | ||
// DestAmount is the max amount of liquidity which exists for a given destination token, provided in the destination token decimals | ||
DestAmount decimal.Decimal `gorm:"column:dest_amount"` | ||
// Price is the price per origin token provided for which a relayer is indicating willingness to relay | ||
Price decimal.Decimal `gorm:"column:price"` | ||
// UpdatedAt is the time that the quote was last upserted | ||
UpdatedAt time.Time `gorm:"column:updated_at"` | ||
} | ||
|
||
// ApiDBReader is the interface for reading from the database. | ||
type ApiDBReader interface { | ||
} | ||
|
||
// ApiDBWriter is the interface for writing to the database. | ||
type ApiDBWriter interface { | ||
} | ||
|
||
// ApiDB is the interface for the database service. | ||
type ApiDB interface { | ||
ApiDBReader | ||
ApiDBWriter | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package db_test | ||
|
||
import "fmt" | ||
|
||
func (d *DBSuite) TestModelCreation() { | ||
fmt.Println("suite started successfully") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package base | ||
|
||
import ( | ||
"github.com/synapsecns/sanguine/core/metrics" | ||
"github.com/synapsecns/sanguine/services/rfq/api/db" | ||
"gorm.io/gorm" | ||
) | ||
|
||
// Store is a store that implements an underlying gorm db. | ||
type Store struct { | ||
db *gorm.DB | ||
metrics metrics.Handler | ||
} | ||
|
||
// NewStore creates a new store. | ||
func NewStore(db *gorm.DB, metrics metrics.Handler) *Store { | ||
return &Store{db: db, metrics: metrics} | ||
} | ||
|
||
// DB gets the database object for mutation outside of the lib. | ||
func (s Store) DB() *gorm.DB { | ||
return s.db | ||
} | ||
|
||
// GetAllModels gets all models to migrate. | ||
// see: https://medium.com/@SaifAbid/slice-interfaces-8c78f8b6345d for an explanation of why we can't do this at initialization time | ||
func GetAllModels() (allModels []interface{}) { | ||
allModels = append(allModels, &db.QuoteModel{}) | ||
return allModels | ||
} | ||
|
||
var _ db.ApiDB = &Store{} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
// Package base contains the base sql implementation | ||
package base |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
// Package sql provides a common interface for starting sql-lite databases | ||
package sql |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
// Package mysql contains a mysql db | ||
package mysql |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package mysql | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"time" | ||
|
||
"github.com/ipfs/go-log" | ||
common_base "github.com/synapsecns/sanguine/core/dbcommon" | ||
"github.com/synapsecns/sanguine/core/metrics" | ||
"github.com/synapsecns/sanguine/services/rfq/api/db" | ||
"github.com/synapsecns/sanguine/services/rfq/api/db/sql/base" | ||
"gorm.io/driver/mysql" | ||
"gorm.io/gorm" | ||
"gorm.io/gorm/schema" | ||
) | ||
|
||
// Logger is the mysql logger. | ||
var logger = log.Logger("api-mysql") | ||
|
||
// NewMysqlStore creates a new mysql store for a given data store. | ||
func NewMysqlStore(ctx context.Context, dbURL string, handler metrics.Handler) (*Store, error) { | ||
logger.Debug("create mysql store") | ||
|
||
gdb, err := gorm.Open(mysql.Open(dbURL), &gorm.Config{ | ||
Logger: common_base.GetGormLogger(logger), | ||
FullSaveAssociations: true, | ||
NamingStrategy: NamingStrategy, | ||
NowFunc: time.Now, | ||
}) | ||
|
||
if err != nil { | ||
return nil, fmt.Errorf("could not create mysql connection: %w", err) | ||
} | ||
|
||
sqlDB, err := gdb.DB() | ||
if err != nil { | ||
return nil, fmt.Errorf("could not get sql db: %w", err) | ||
} | ||
|
||
// fixes a timeout issue https://stackoverflow.com/a/42146536 | ||
sqlDB.SetMaxIdleConns(MaxIdleConns) | ||
sqlDB.SetConnMaxLifetime(time.Hour) | ||
|
||
handler.AddGormCallbacks(gdb) | ||
|
||
err = gdb.WithContext(ctx).AutoMigrate(base.GetAllModels()...) | ||
if err != nil { | ||
return nil, fmt.Errorf("could not migrate on mysql: %w", err) | ||
} | ||
|
||
return &Store{base.NewStore(gdb, handler)}, nil | ||
} | ||
|
||
// Store is the mysql store. It extends the bsae store for mysql queries. | ||
type Store struct { | ||
*base.Store | ||
} | ||
|
||
// MaxIdleConns is exported here for testing. Tests execute too slowly with a reconnect each time. | ||
var MaxIdleConns = 10 | ||
|
||
// NamingStrategy is for table prefixes. | ||
var NamingStrategy = schema.NamingStrategy{} | ||
|
||
var _ db.ApiDB = &Store{} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
// Package sqlite implements the sqlite package | ||
package sqlite |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package sqlite | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"os" | ||
|
||
"github.com/synapsecns/sanguine/services/rfq/api/db/sql/base" | ||
|
||
"github.com/ipfs/go-log" | ||
common_base "github.com/synapsecns/sanguine/core/dbcommon" | ||
"github.com/synapsecns/sanguine/core/metrics" | ||
"github.com/synapsecns/sanguine/services/rfq/api/db" | ||
"gorm.io/driver/sqlite" | ||
"gorm.io/gorm" | ||
) | ||
|
||
// Store is the sqlite store. It extends the base store for sqlite specific queries. | ||
type Store struct { | ||
*base.Store | ||
} | ||
|
||
var logger = log.Logger("api-sqlite") | ||
|
||
// NewSqliteStore creates a new sqlite data store. | ||
func NewSqliteStore(parentCtx context.Context, dbPath string, handler metrics.Handler, skipMigrations bool) (_ *Store, err error) { | ||
logger.Debugf("creating sqlite store at %s", dbPath) | ||
|
||
ctx, span := handler.Tracer().Start(parentCtx, "start-sqlite") | ||
defer func() { | ||
metrics.EndSpanWithErr(span, err) | ||
}() | ||
|
||
// create the directory to the store if it doesn't exist | ||
err = os.MkdirAll(dbPath, os.ModePerm) | ||
if err != nil { | ||
return nil, fmt.Errorf("could not create sqlite store") | ||
} | ||
|
||
logger.Warnf("api database is at %s/api.db", dbPath) | ||
|
||
gdb, err := gorm.Open(sqlite.Open(fmt.Sprintf("%s/%s", dbPath, "api.db")), &gorm.Config{ | ||
DisableForeignKeyConstraintWhenMigrating: true, | ||
Logger: common_base.GetGormLogger(logger), | ||
FullSaveAssociations: true, | ||
SkipDefaultTransaction: true, | ||
}) | ||
if err != nil { | ||
return nil, fmt.Errorf("could not connect to db %s: %w", dbPath, err) | ||
} | ||
|
||
handler.AddGormCallbacks(gdb) | ||
|
||
if !skipMigrations { | ||
err = gdb.WithContext(ctx).AutoMigrate(base.GetAllModels()...) | ||
if err != nil { | ||
return nil, fmt.Errorf("could not migrate models: %w", err) | ||
} | ||
} | ||
return &Store{base.NewStore(gdb, handler)}, nil | ||
} | ||
|
||
var _ db.ApiDB = &Store{} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package sql | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
|
||
"github.com/synapsecns/sanguine/core/dbcommon" | ||
"github.com/synapsecns/sanguine/core/metrics" | ||
"github.com/synapsecns/sanguine/services/rfq/api/db" | ||
"github.com/synapsecns/sanguine/services/rfq/api/db/sql/mysql" | ||
"github.com/synapsecns/sanguine/services/rfq/api/db/sql/sqlite" | ||
) | ||
|
||
// Connect connects to the database. | ||
func Connect(ctx context.Context, dbType dbcommon.DBType, path string, metrics metrics.Handler) (db.ApiDB, error) { | ||
switch dbType { | ||
case dbcommon.Mysql: | ||
store, err := mysql.NewMysqlStore(ctx, path, metrics) | ||
if err != nil { | ||
return nil, fmt.Errorf("could not create mysql store: %w", err) | ||
} | ||
|
||
return store, nil | ||
case dbcommon.Sqlite: | ||
store, err := sqlite.NewSqliteStore(ctx, path, metrics, false) | ||
if err != nil { | ||
return nil, fmt.Errorf("could not create sqlite store: %w", err) | ||
} | ||
|
||
return store, nil | ||
case dbcommon.Clickhouse: | ||
return nil, errors.New("driver not supported") | ||
default: | ||
return nil, fmt.Errorf("unsupported driver: %s", dbType) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
package db_test | ||
|
||
import ( | ||
dbSQL "database/sql" | ||
"fmt" | ||
"os" | ||
"sync" | ||
"testing" | ||
|
||
"github.com/Flaque/filet" | ||
. "github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/suite" | ||
"github.com/synapsecns/sanguine/core" | ||
"github.com/synapsecns/sanguine/core/dbcommon" | ||
"github.com/synapsecns/sanguine/core/metrics" | ||
"github.com/synapsecns/sanguine/core/metrics/localmetrics" | ||
"github.com/synapsecns/sanguine/core/testsuite" | ||
"github.com/synapsecns/sanguine/services/rfq/api/db" | ||
"github.com/synapsecns/sanguine/services/rfq/api/db/sql" | ||
"github.com/synapsecns/sanguine/services/rfq/api/db/sql/mysql" | ||
"github.com/synapsecns/sanguine/services/rfq/api/metadata" | ||
"gorm.io/gorm/schema" | ||
) | ||
|
||
type DBSuite struct { | ||
*testsuite.TestSuite | ||
dbs []db.ApiDB | ||
metrics metrics.Handler | ||
} | ||
|
||
// NewDBSuite creates a new DBSuite. | ||
func NewDBSuite(tb testing.TB) *DBSuite { | ||
tb.Helper() | ||
return &DBSuite{ | ||
TestSuite: testsuite.NewTestSuite(tb), | ||
dbs: []db.ApiDB{}, | ||
} | ||
} | ||
func (d *DBSuite) SetupSuite() { | ||
d.TestSuite.SetupSuite() | ||
|
||
// don't use metrics on ci for integration tests | ||
isCI := core.GetEnvBool("CI", false) | ||
useMetrics := !isCI | ||
metricsHandler := metrics.Null | ||
|
||
if useMetrics { | ||
localmetrics.SetupTestJaeger(d.GetSuiteContext(), d.T()) | ||
metricsHandler = metrics.Jaeger | ||
} | ||
|
||
var err error | ||
d.metrics, err = metrics.NewByType(d.GetSuiteContext(), metadata.BuildInfo(), metricsHandler) | ||
Nil(d.T(), err) | ||
} | ||
|
||
func (d *DBSuite) SetupTest() { | ||
d.TestSuite.SetupTest() | ||
|
||
sqliteStore, err := sql.Connect(d.GetTestContext(), dbcommon.Sqlite, filet.TmpDir(d.T(), ""), d.metrics) | ||
Nil(d.T(), err) | ||
|
||
d.dbs = []db.ApiDB{sqliteStore} | ||
d.setupMysqlDB() | ||
} | ||
|
||
func (d *DBSuite) setupMysqlDB() { | ||
if os.Getenv(dbcommon.EnableMysqlTestVar) != "true" { | ||
return | ||
} | ||
|
||
mysql.NamingStrategy = schema.NamingStrategy{ | ||
TablePrefix: fmt.Sprintf("api_%d", d.GetTestID()), | ||
} | ||
|
||
// sets up the conn string to the default database | ||
connString := dbcommon.GetTestConnString() | ||
// sets up the myqsl db | ||
testDB, err := dbSQL.Open("mysql", connString) | ||
d.Require().NoError(err) | ||
// close the db once the connection is don | ||
defer func() { | ||
d.Require().NoError(testDB.Close()) | ||
}() | ||
|
||
mysqlStore, err := mysql.NewMysqlStore(d.GetTestContext(), connString, d.metrics) | ||
d.Require().NoError(err) | ||
|
||
d.dbs = append(d.dbs, mysqlStore) | ||
} | ||
|
||
func (d *DBSuite) RunOnAllDBs(testFunc func(testDB db.ApiDB)) { | ||
d.T().Helper() | ||
|
||
wg := sync.WaitGroup{} | ||
for _, testDB := range d.dbs { | ||
wg.Add(1) | ||
// capture the value | ||
go func(testDB db.ApiDB) { | ||
defer wg.Done() | ||
testFunc(testDB) | ||
}(testDB) | ||
} | ||
wg.Wait() | ||
} | ||
|
||
func TestDBSuite(t *testing.T) { | ||
suite.Run(t, NewDBSuite(t)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// Package metadata provides a metadata service for the RFQ API. | ||
package metadata | ||
|
||
import "github.com/synapsecns/sanguine/core/config" | ||
|
||
var ( | ||
version = config.DefaultVersion | ||
commit = config.DefaultCommit | ||
date = config.DefaultDate | ||
) | ||
|
||
// BuildInfo returns the build info for the service. | ||
func BuildInfo() config.BuildInfo { | ||
return config.NewBuildInfo(version, commit, "rfq-api", date) | ||
} |