Encryption At Rest (#284)
This commit is contained in:
parent
d657ec03a6
commit
77cb81435c
|
@ -235,3 +235,28 @@ curl -H "Authorization: Bearer ${JWT_TOKEN_HERE}" http://localhost:5984/_session
|
|||
```bash
|
||||
ng run fastenhealth:storybook
|
||||
```
|
||||
|
||||
|
||||
# Access Encrypted SQLite Database with IntelliJ
|
||||
|
||||
- Download the latest `sqlite-jdbc-crypt` jar from https://github.com/Willena/sqlite-jdbc-crypt/releases
|
||||
- Open IntelliJ -> Data Source Properties -> Driver Tab
|
||||
- Find & Select `Sqlite` -> Right Click -> Duplicate
|
||||
- Rename to `Sqlite (Encrypted)`
|
||||
- Find `Driver Files` -> Select `sqlite-jdbc-crypt` jar that you downloaded previously
|
||||
- Remove `Xerial Sqlite JDBC` jar
|
||||
- Click `Apply` -> Click `OK`
|
||||
- Create New Data Source -> Select `Sqlite (Encrypted)` -> Change Connection Type to `Url only`
|
||||
- Specify the following connection url: `jdbc:sqlite:fasten.db?cipher=sqlcipher&legacy=3&hmac_use=0&kdf_iter=4000&legacy_page_size=1024&key=123456789012345678901234567890`
|
||||
- Replace `key` with the encryption key specified in your config file (`database.encryption_key`)
|
||||
- Click `Test Connection` -> Should be successful
|
||||
- Click `Apply` -> Click `OK`
|
||||
|
||||
# Flush SQLite Write-Ahead-Log (WAL) to Database
|
||||
|
||||
```sqlite
|
||||
PRAGMA wal_checkpoint(TRUNCATE);
|
||||
```
|
||||
|
||||
See: https://sqlite.org/forum/info/fefd56014e2135589ea57825b0e2aa3e2df5daf53b5e41aa6a9d8f0c29d0b8e5
|
||||
TODO: check if https://www.sqlite.org/pragma.html#pragma_wal_checkpoint can be used to do this automatically.
|
||||
|
|
|
@ -32,6 +32,7 @@ func (c *configuration) Init() error {
|
|||
c.SetDefault("web.src.frontend.path", "/opt/fasten/web")
|
||||
c.SetDefault("database.type", "sqlite")
|
||||
c.SetDefault("database.location", "/opt/fasten/db/fasten.db")
|
||||
//c.SetDefault("database.encryption.key", "") //encryption key must be set by the user.
|
||||
c.SetDefault("cache.location", "/opt/fasten/cache/")
|
||||
|
||||
c.SetDefault("jwt.issuer.key", "thisismysupersecuressessionsecretlength")
|
||||
|
@ -80,6 +81,14 @@ func (c *configuration) ReadConfig(configFilePath string) error {
|
|||
|
||||
// This function ensures that required configuration keys (that must be manually set) are present
|
||||
func (c *configuration) ValidateConfig() error {
|
||||
|
||||
if c.IsSet("database.encryption.key") {
|
||||
key := c.GetString("database.encryption.key")
|
||||
if key == "" {
|
||||
return errors.ConfigValidationError("database.encryption.key cannot be empty")
|
||||
}
|
||||
if len(key) < 10 {
|
||||
return errors.ConfigValidationError("database.encryption.key must be at least 10 characters")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"github.com/fastenhealth/fasten-onprem/backend/pkg/errors"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_ValidateConfig(t *testing.T) {
|
||||
//setup
|
||||
testConfig := configuration{
|
||||
Viper: viper.New(),
|
||||
}
|
||||
|
||||
//test & verify
|
||||
testConfig.Set("database.encryption.key", "tooshort")
|
||||
err := testConfig.ValidateConfig()
|
||||
require.ErrorIs(t, err, errors.ConfigValidationError("database.encryption.key must be at least 10 characters"))
|
||||
|
||||
testConfig.Set("database.encryption.key", "")
|
||||
err = testConfig.ValidateConfig()
|
||||
require.ErrorIs(t, err, errors.ConfigValidationError("database.encryption.key cannot be empty"))
|
||||
|
||||
}
|
|
@ -5,7 +5,6 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -485,7 +484,7 @@ func (gr *GormRepository) AddResourceAssociation(ctx context.Context, source *mo
|
|||
"related_resource_source_resource_type": relatedResourceType,
|
||||
"related_resource_source_resource_id": relatedResourceId,
|
||||
}).Error
|
||||
uniqueConstraintError := errors.New("constraint failed: UNIQUE constraint failed")
|
||||
uniqueConstraintError := errors.New("UNIQUE constraint failed")
|
||||
if err != nil {
|
||||
if strings.HasPrefix(err.Error(), uniqueConstraintError.Error()) {
|
||||
gr.Logger.Warnf("Ignoring an error when creating a related_resource association for %s/%s: %v", resourceType, resourceId, err)
|
||||
|
@ -1002,8 +1001,11 @@ func (gr *GormRepository) ListBackgroundJobs(ctx context.Context, queryOptions m
|
|||
var backgroundJobs []models.BackgroundJob
|
||||
query := gr.GormClient.WithContext(ctx).
|
||||
//Group("source_id"). //broken in Postgres.
|
||||
Where(queryParam).Limit(queryOptions.Limit).Order("locked_time DESC")
|
||||
Where(queryParam).Order("locked_time DESC")
|
||||
|
||||
if queryOptions.Limit > 0 {
|
||||
query = query.Limit(queryOptions.Limit)
|
||||
}
|
||||
if queryOptions.Offset > 0 {
|
||||
query = query.Offset(queryOptions.Offset)
|
||||
}
|
||||
|
@ -1119,19 +1121,6 @@ func (gr *GormRepository) CancelAllLockedBackgroundJobsAndFail() error {
|
|||
// Utilities
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func sqlitePragmaString(pragmas map[string]string) string {
|
||||
q := url.Values{}
|
||||
for key, val := range pragmas {
|
||||
q.Add("_pragma", fmt.Sprintf("%s=%s", key, val))
|
||||
}
|
||||
|
||||
queryStr := q.Encode()
|
||||
if len(queryStr) > 0 {
|
||||
return "?" + queryStr
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Internal function
|
||||
// This function will return a list of resources from all FHIR tables in the database
|
||||
// The query allows us to set the source id, source resource id, source resource type
|
||||
|
|
|
@ -44,6 +44,7 @@ func (suite *RepositorySqlTestSuite) BeforeTest(suiteName, testName string) {
|
|||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
|
@ -62,6 +63,8 @@ func (suite *RepositorySqlTestSuite) BeforeTest(suiteName, testName string) {
|
|||
func (suite *RepositorySqlTestSuite) AfterTest(suiteName, testName string) {
|
||||
suite.MockCtrl.Finish()
|
||||
os.Remove(suite.TestDatabase.Name())
|
||||
os.Remove(suite.TestDatabase.Name() + "-shm")
|
||||
os.Remove(suite.TestDatabase.Name() + "-wal")
|
||||
}
|
||||
|
||||
// In order for 'go test' to run this suite, we need to create
|
||||
|
|
|
@ -261,6 +261,7 @@ func (suite *RepositoryTestSuite) TestQueryResources_SQL() {
|
|||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
|
|
|
@ -61,7 +61,9 @@ func (suite *RepositoryTestSuite) BeforeTest(suiteName, testName string) {
|
|||
// AfterTest has a function to be executed right after the test finishes and receives the suite and test names as input
|
||||
func (suite *RepositoryTestSuite) AfterTest(suiteName, testName string) {
|
||||
suite.MockCtrl.Finish()
|
||||
os.Remove(suite.TestDatabase.Name())
|
||||
//os.Remove(suite.TestDatabase.Name())
|
||||
//os.Remove(suite.TestDatabase.Name() + "-shm")
|
||||
//os.Remove(suite.TestDatabase.Name() + "-wal")
|
||||
}
|
||||
|
||||
// In order for 'go test' to run this suite, we need to create
|
||||
|
@ -76,6 +78,7 @@ func (suite *RepositoryTestSuite) TestNewRepository() {
|
|||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
|
||||
//test
|
||||
|
@ -90,6 +93,7 @@ func (suite *RepositoryTestSuite) TestCreateUser() {
|
|||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
|
@ -111,6 +115,7 @@ func (suite *RepositoryTestSuite) TestCreateUser_WithExitingUser_ShouldFail() {
|
|||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
|
@ -140,6 +145,7 @@ func (suite *RepositoryTestSuite) TestCreateUser_WithUserProvidedId_ShouldBeRepl
|
|||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
|
@ -166,6 +172,7 @@ func (suite *RepositoryTestSuite) TestGetUserByUsername() {
|
|||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
|
@ -190,6 +197,7 @@ func (suite *RepositoryTestSuite) TestGetUserByUsername_WithInvalidUsername() {
|
|||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
|
@ -213,6 +221,7 @@ func (suite *RepositoryTestSuite) TestGetCurrentUser_WithContextBackgroundAuthUs
|
|||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
|
@ -238,6 +247,7 @@ func (suite *RepositoryTestSuite) TestGetCurrentUser_WithGinContextBackgroundAut
|
|||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
|
@ -267,6 +277,7 @@ func (suite *RepositoryTestSuite) TestGetCurrentUser_WithContextBackgroundAuthUs
|
|||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
|
@ -284,6 +295,7 @@ func (suite *RepositoryTestSuite) TestCreateGlossaryEntry() {
|
|||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
|
@ -320,6 +332,7 @@ func (suite *RepositoryTestSuite) TestUpsertRawResource() {
|
|||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
|
@ -377,6 +390,7 @@ func (suite *RepositoryTestSuite) TestUpsertRawResource_WithRelatedResourceAndDu
|
|||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
|
@ -425,6 +439,7 @@ func (suite *RepositoryTestSuite) TestListResources() {
|
|||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
|
@ -528,6 +543,7 @@ func (suite *RepositoryTestSuite) TestGetResourceByResourceTypeAndId() {
|
|||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
|
@ -582,6 +598,7 @@ func (suite *RepositoryTestSuite) TestGetResourceBySourceId() {
|
|||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
|
@ -636,6 +653,7 @@ func (suite *RepositoryTestSuite) TestGetPatientForSources() {
|
|||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
|
@ -693,6 +711,7 @@ func (suite *RepositoryTestSuite) TestAddResourceAssociation() {
|
|||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
|
@ -730,6 +749,7 @@ func (suite *RepositoryTestSuite) TestAddResourceAssociation_WithMismatchingSour
|
|||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
|
@ -780,6 +800,7 @@ func (suite *RepositoryTestSuite) TestRemoveResourceAssociation() {
|
|||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
|
@ -822,6 +843,7 @@ func (suite *RepositoryTestSuite) TestGetSourceSummary() {
|
|||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
|
@ -903,6 +925,7 @@ func (suite *RepositoryTestSuite) TestGetSummary() {
|
|||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
|
@ -1007,6 +1030,7 @@ func (suite *RepositoryTestSuite) TestAddResourceComposition() {
|
|||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
|
@ -1093,6 +1117,7 @@ func (suite *RepositoryTestSuite) TestAddResourceComposition_WithExistingComposi
|
|||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
|
@ -1254,6 +1279,7 @@ func (suite *RepositoryTestSuite) TestCreateBackgroundJob_Sync() {
|
|||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
|
@ -1289,7 +1315,8 @@ func (suite *RepositoryTestSuite) TestListBackgroundJobs() {
|
|||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("DEBUG").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
|
||||
|
@ -1300,7 +1327,7 @@ func (suite *RepositoryTestSuite) TestListBackgroundJobs() {
|
|||
}
|
||||
err = dbRepo.CreateUser(context.Background(), userModel)
|
||||
require.NoError(suite.T(), err)
|
||||
authContext := context.WithValue(context.Background(), pkg.ContextKeyTypeAuthUsername, "test_username")
|
||||
authContext := context.WithValue(context.Background(), pkg.ContextKeyTypeAuthUsername, userModel.Username)
|
||||
|
||||
otherUserModel := &models.User{
|
||||
Username: "test_other_username",
|
||||
|
@ -1319,24 +1346,25 @@ func (suite *RepositoryTestSuite) TestListBackgroundJobs() {
|
|||
|
||||
backgroundJob := models.NewSyncBackgroundJob(testSourceCredential)
|
||||
err = dbRepo.CreateBackgroundJob(
|
||||
context.WithValue(authContext, pkg.ContextKeyTypeAuthUsername, "test_username"),
|
||||
authContext,
|
||||
backgroundJob,
|
||||
)
|
||||
require.NoError(suite.T(), err)
|
||||
|
||||
backgroundJob2 := models.NewSyncBackgroundJob(testSourceCredential)
|
||||
backgroundJob2.JobType = pkg.BackgroundJobTypeScheduledSync
|
||||
err = dbRepo.CreateBackgroundJob(
|
||||
context.WithValue(authContext, pkg.ContextKeyTypeAuthUsername, "test_username"),
|
||||
authContext,
|
||||
backgroundJob2,
|
||||
)
|
||||
require.NoError(suite.T(), err)
|
||||
|
||||
backgroundJob3 := models.NewSyncBackgroundJob(testSourceCredential)
|
||||
backgroundJob3.JobStatus = pkg.BackgroundJobStatusFailed
|
||||
err = dbRepo.CreateBackgroundJob(
|
||||
context.WithValue(authContext, pkg.ContextKeyTypeAuthUsername, "test_username"),
|
||||
authContext,
|
||||
backgroundJob3,
|
||||
)
|
||||
|
||||
require.NoError(suite.T(), err)
|
||||
|
||||
//test
|
||||
|
@ -1356,9 +1384,9 @@ func (suite *RepositoryTestSuite) TestListBackgroundJobs() {
|
|||
require.NoError(suite.T(), err)
|
||||
|
||||
//assert
|
||||
require.Equal(suite.T(), len(foundAllBackgroundJobs), 3)
|
||||
require.Equal(suite.T(), len(foundBackgroundJobsByType), 2)
|
||||
require.Equal(suite.T(), len(foundBackgroundJobsByStatus), 1)
|
||||
require.Equal(suite.T(), 3, len(foundAllBackgroundJobs))
|
||||
require.Equal(suite.T(), 2, len(foundBackgroundJobsByType))
|
||||
require.Equal(suite.T(), 1, len(foundBackgroundJobsByStatus))
|
||||
}
|
||||
|
||||
func (suite *RepositoryTestSuite) TestUpdateBackgroundJob() {
|
||||
|
@ -1366,6 +1394,7 @@ func (suite *RepositoryTestSuite) TestUpdateBackgroundJob() {
|
|||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
|
|
|
@ -2,16 +2,19 @@ package database
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/fastenhealth/fasten-onprem/backend/pkg/config"
|
||||
"github.com/fastenhealth/fasten-onprem/backend/pkg/event_bus"
|
||||
"github.com/fastenhealth/fasten-onprem/backend/pkg/models"
|
||||
"github.com/glebarez/sqlite"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
//"github.com/glebarez/sqlite"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// uses github.com/mattn/go-sqlite3 driver (warning, uses CGO)
|
||||
func newSqliteRepository(appConfig config.Interface, globalLogger logrus.FieldLogger, eventBus event_bus.Interface) (DatabaseRepository, error) {
|
||||
//backgroundContext := context.Background()
|
||||
|
||||
|
@ -37,11 +40,30 @@ func newSqliteRepository(appConfig config.Interface, globalLogger logrus.FieldLo
|
|||
// In this case all writes are appended to a temporary file (write-ahead log) and this file is periodically merged with the original database. When SQLite is searching for something it would first check this temporary file and if nothing is found proceed with the main database file.
|
||||
// As a result, readers don’t compete with writers and performance is much better compared to the Old Way.
|
||||
// https://stackoverflow.com/questions/4060772/sqlite-concurrent-access
|
||||
pragmaStr := sqlitePragmaString(map[string]string{
|
||||
"busy_timeout": "5000",
|
||||
"foreign_keys": "ON",
|
||||
"journal_mode": "wal",
|
||||
})
|
||||
//
|
||||
// NOTE: this schema is driver specific, and may not work with other drivers.
|
||||
// eg.https://github.com/mattn/go-sqlite3 uses `?_journal_mode=WAL` prefixes
|
||||
// https://github.com/glebarez/sqlite uses `?_pragma=journal_mode(WAL)`
|
||||
// see https://github.com/mattn/go-sqlite3/compare/master...jgiannuzzi:go-sqlite3:sqlite3mc
|
||||
// see https://github.com/mattn/go-sqlite3/pull/1109
|
||||
pragmaOpts := map[string]string{
|
||||
"_busy_timeout": "5000",
|
||||
"_foreign_keys": "on",
|
||||
"_journal_mode": "WAL",
|
||||
}
|
||||
|
||||
//validation of encryption key happens in ValidateConfig method
|
||||
if appConfig.IsSet("database.encryption.key") {
|
||||
pragmaOpts["_cipher"] = "sqlcipher"
|
||||
pragmaOpts["_legacy"] = "3"
|
||||
pragmaOpts["_hmac_use"] = "off"
|
||||
pragmaOpts["_kdf_iter"] = "4000"
|
||||
pragmaOpts["_legacy_page_size"] = "1024"
|
||||
pragmaOpts["_key"] = appConfig.GetString("database.encryption.key")
|
||||
}
|
||||
|
||||
pragmaStr := sqlitePragmaString(pragmaOpts)
|
||||
|
||||
dsn := "file:" + appConfig.GetString("database.location") + pragmaStr
|
||||
database, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{
|
||||
//TODO: figure out how to log database queries again.
|
||||
|
@ -49,13 +71,16 @@ func newSqliteRepository(appConfig config.Interface, globalLogger logrus.FieldLo
|
|||
DisableForeignKeyConstraintWhenMigrating: true,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "file is not a database") {
|
||||
return nil, fmt.Errorf("failed to connect to database! encryption key may be incorrect - %w", err)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("failed to connect to database! - %w", err)
|
||||
}
|
||||
if strings.ToUpper(appConfig.GetString("log.level")) == "DEBUG" {
|
||||
database = database.Debug() //set debug globally
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to connect to database! - %v", err)
|
||||
}
|
||||
globalLogger.Infof("Successfully connected to fasten sqlite db: %s\n", dsn)
|
||||
|
||||
////verify journal mode
|
||||
|
@ -96,3 +121,17 @@ func newSqliteRepository(appConfig config.Interface, globalLogger logrus.FieldLo
|
|||
|
||||
return &fastenRepo, nil
|
||||
}
|
||||
|
||||
func sqlitePragmaString(pragmas map[string]string) string {
|
||||
q := url.Values{}
|
||||
for key, val := range pragmas {
|
||||
//q.Add("_pragma", fmt.Sprintf("%s=%s", key, val))
|
||||
q.Add(key, val)
|
||||
}
|
||||
|
||||
queryStr := q.Encode()
|
||||
if len(queryStr) > 0 {
|
||||
return "?" + queryStr
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
|
|
@ -0,0 +1,264 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
mock_config "github.com/fastenhealth/fasten-onprem/backend/pkg/config/mock"
|
||||
"github.com/fastenhealth/fasten-onprem/backend/pkg/event_bus"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Define the suite, and absorb the built-in basic suite
|
||||
// functionality from testify - including a T() method which
|
||||
// returns the current testing context
|
||||
type SqliteRepositoryTestSuite struct {
|
||||
suite.Suite
|
||||
MockCtrl *gomock.Controller
|
||||
TestDatabase *os.File
|
||||
}
|
||||
|
||||
// BeforeTest has a function to be executed right before the test starts and receives the suite and test names as input
|
||||
func (suite *SqliteRepositoryTestSuite) BeforeTest(suiteName, testName string) {
|
||||
suite.MockCtrl = gomock.NewController(suite.T())
|
||||
|
||||
dbFile, err := ioutil.TempFile("", fmt.Sprintf("%s.*.db", testName))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
suite.TestDatabase = dbFile
|
||||
|
||||
}
|
||||
|
||||
// AfterTest has a function to be executed right after the test finishes and receives the suite and test names as input
|
||||
func (suite *SqliteRepositoryTestSuite) AfterTest(suiteName, testName string) {
|
||||
suite.MockCtrl.Finish()
|
||||
os.Remove(suite.TestDatabase.Name())
|
||||
os.Remove(suite.TestDatabase.Name() + "-shm")
|
||||
os.Remove(suite.TestDatabase.Name() + "-wal")
|
||||
}
|
||||
|
||||
// In order for 'go test' to run this suite, we need to create
|
||||
// a normal test function and pass our suite to suite.Run
|
||||
func TestSqliteRepositoryTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(SqliteRepositoryTestSuite))
|
||||
|
||||
}
|
||||
|
||||
// Scenario 0: repository creation with default settings (no encryption)
|
||||
func (suite *SqliteRepositoryTestSuite) TestNewSqliteRepository_DefaultPragmaSettings() {
|
||||
//setup
|
||||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
|
||||
repo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
sqliteRepo, sqliteRepoOk := repo.(*GormRepository)
|
||||
require.True(suite.T(), sqliteRepoOk)
|
||||
|
||||
//test
|
||||
var journalMode string
|
||||
resp := sqliteRepo.GormClient.Raw("PRAGMA journal_mode;").Scan(&journalMode)
|
||||
require.NoError(suite.T(), resp.Error)
|
||||
|
||||
var busyTimeout int
|
||||
resp = sqliteRepo.GormClient.Raw("PRAGMA busy_timeout;").Scan(&busyTimeout)
|
||||
require.NoError(suite.T(), resp.Error)
|
||||
|
||||
var foreignKeys bool
|
||||
resp = sqliteRepo.GormClient.Raw("PRAGMA foreign_keys;").Scan(&foreignKeys)
|
||||
require.NoError(suite.T(), resp.Error)
|
||||
|
||||
//assert
|
||||
require.Equal(suite.T(), "wal", journalMode)
|
||||
require.Equal(suite.T(), 5000, busyTimeout)
|
||||
require.Equal(suite.T(), true, foreignKeys)
|
||||
}
|
||||
|
||||
// Scenario 1: repository creation with encryption
|
||||
func (suite *SqliteRepositoryTestSuite) TestNewSqliteRepository_WithEncryptionKey() {
|
||||
//setup
|
||||
fakeConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(true).AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("database.encryption.key").Return("012345678901234567890").AnyTimes()
|
||||
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
|
||||
repo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
sqliteRepo, sqliteRepoOk := repo.(*GormRepository)
|
||||
require.True(suite.T(), sqliteRepoOk)
|
||||
|
||||
//test
|
||||
var cipher string
|
||||
resp := sqliteRepo.GormClient.Raw("PRAGMA cipher;").Scan(&cipher)
|
||||
require.NoError(suite.T(), resp.Error)
|
||||
|
||||
var legacy int
|
||||
resp = sqliteRepo.GormClient.Raw("PRAGMA legacy;").Scan(&legacy)
|
||||
require.NoError(suite.T(), resp.Error)
|
||||
|
||||
var hmacUse bool
|
||||
resp = sqliteRepo.GormClient.Raw("PRAGMA hmac_use;").Scan(&hmacUse)
|
||||
require.NoError(suite.T(), resp.Error)
|
||||
|
||||
var kdfIter int
|
||||
resp = sqliteRepo.GormClient.Raw("PRAGMA kdf_iter;").Scan(&kdfIter)
|
||||
require.NoError(suite.T(), resp.Error)
|
||||
|
||||
var legacyPageSize int
|
||||
resp = sqliteRepo.GormClient.Raw("PRAGMA legacy_page_size;").Scan(&legacyPageSize)
|
||||
require.NoError(suite.T(), resp.Error)
|
||||
|
||||
//assert
|
||||
require.Equal(suite.T(), "sqlcipher", cipher)
|
||||
require.Equal(suite.T(), 3, legacy)
|
||||
require.Equal(suite.T(), 1024, legacyPageSize)
|
||||
require.Equal(suite.T(), false, hmacUse)
|
||||
require.Equal(suite.T(), 4000, kdfIter)
|
||||
}
|
||||
|
||||
// Scenario 2: repository creation with encryption key, closing the app, and then reopening with a changed/incorrect key
|
||||
func (suite *SqliteRepositoryTestSuite) TestNewSqliteRepository_WithEncryptionKey_WhenReopenWithIncorrectKeyShouldFail() {
|
||||
//setup
|
||||
fakeConfig1 := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig1.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig1.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig1.EXPECT().IsSet("database.encryption.key").Return(true).AnyTimes()
|
||||
fakeConfig1.EXPECT().GetString("database.encryption.key").Return("012345678901234567890").AnyTimes()
|
||||
fakeConfig1.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
|
||||
//intialize the database with a key
|
||||
_, err := NewRepository(fakeConfig1, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
|
||||
fakeConfig2 := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig2.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig2.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig2.EXPECT().IsSet("database.encryption.key").Return(true).AnyTimes()
|
||||
fakeConfig2.EXPECT().GetString("database.encryption.key").Return("incorrect_key_here").AnyTimes()
|
||||
fakeConfig2.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
|
||||
//test
|
||||
_, err2 := NewRepository(fakeConfig2, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
|
||||
//assert
|
||||
require.Equal(suite.T(), "failed to connect to database! encryption key may be incorrect - file is not a database", err2.Error())
|
||||
}
|
||||
|
||||
// Scenario 3: repository creation with encryption key, closing the app, and then reopening with the correct key
|
||||
func (suite *SqliteRepositoryTestSuite) TestNewSqliteRepository_WithEncryptionKey_WhenReopenWithCorrectKeyShouldPass() {
|
||||
//setup
|
||||
fakeConfig1 := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig1.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig1.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig1.EXPECT().IsSet("database.encryption.key").Return(true).AnyTimes()
|
||||
fakeConfig1.EXPECT().GetString("database.encryption.key").Return("012345678901234567890").AnyTimes()
|
||||
fakeConfig1.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
|
||||
//intialize the database with a key
|
||||
_, err := NewRepository(fakeConfig1, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
|
||||
fakeConfig2 := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig2.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig2.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig2.EXPECT().IsSet("database.encryption.key").Return(true).AnyTimes()
|
||||
fakeConfig2.EXPECT().GetString("database.encryption.key").Return("012345678901234567890").AnyTimes()
|
||||
fakeConfig2.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
|
||||
//test
|
||||
_, err2 := NewRepository(fakeConfig2, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
|
||||
//assert
|
||||
require.NoError(suite.T(), err2)
|
||||
}
|
||||
|
||||
// Scenario 4: repository creation with encryption key, closing the app, and then reopening with deaults (no encryption)
|
||||
func (suite *SqliteRepositoryTestSuite) TestNewSqliteRepository_WithEncryptionKey_WhenReopenWithNoEncryptionKeyShouldFail() {
|
||||
//setup
|
||||
fakeConfig1 := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig1.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig1.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig1.EXPECT().IsSet("database.encryption.key").Return(true).AnyTimes()
|
||||
fakeConfig1.EXPECT().GetString("database.encryption.key").Return("012345678901234567890").AnyTimes()
|
||||
fakeConfig1.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
|
||||
//intialize the database with a key
|
||||
_, err := NewRepository(fakeConfig1, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
|
||||
fakeConfig2 := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig2.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig2.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig2.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
fakeConfig2.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
|
||||
//test
|
||||
_, err2 := NewRepository(fakeConfig2, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
|
||||
//assert
|
||||
require.Equal(suite.T(), "failed to connect to database! encryption key may be incorrect - file is not a database", err2.Error())
|
||||
}
|
||||
|
||||
// Scenario 5: repository creation without encryption key (defaults), closing the app, and then reopening with an encryption key
|
||||
func (suite *SqliteRepositoryTestSuite) TestNewSqliteRepository_WithoutEncryption_WhenReopenWithEncryptionKeyShouldFail() {
|
||||
//setup
|
||||
fakeConfig1 := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig1.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig1.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig1.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
fakeConfig1.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
|
||||
//intialize the database with a key
|
||||
_, err := NewRepository(fakeConfig1, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
|
||||
fakeConfig2 := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig2.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig2.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig2.EXPECT().IsSet("database.encryption.key").Return(true).AnyTimes()
|
||||
fakeConfig2.EXPECT().GetString("database.encryption.key").Return("012345678901234567890").AnyTimes()
|
||||
fakeConfig2.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
|
||||
//test
|
||||
_, err2 := NewRepository(fakeConfig2, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
|
||||
//assert
|
||||
require.Equal(suite.T(), "failed to connect to database! encryption key may be incorrect - file is not a database", err2.Error())
|
||||
}
|
||||
|
||||
// Scenario 6: repository creation without encryption key (defaults), closing the app, and then reopening without an encryption key
|
||||
func (suite *SqliteRepositoryTestSuite) TestNewSqliteRepository_WithoutEncryption_WhenReopenWithoutEncryptionKeyShouldPass() {
|
||||
//setup
|
||||
fakeConfig1 := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig1.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig1.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig1.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
fakeConfig1.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
|
||||
//intialize the database with a key
|
||||
_, err := NewRepository(fakeConfig1, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
require.NoError(suite.T(), err)
|
||||
|
||||
fakeConfig2 := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
fakeConfig2.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
fakeConfig2.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
fakeConfig2.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
fakeConfig2.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
|
||||
//test
|
||||
_, err2 := NewRepository(fakeConfig2, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
|
||||
//assert
|
||||
require.NoError(suite.T(), err2)
|
||||
}
|
|
@ -33,35 +33,35 @@ type ResourcFhirHandlerTestSuite struct {
|
|||
AppConfig *mock_config.MockInterface
|
||||
AppRepository database.DatabaseRepository
|
||||
AppEventBus event_bus.Interface
|
||||
SourceId uuid.UUID
|
||||
SourceId uuid.UUID
|
||||
}
|
||||
|
||||
|
||||
func (suite *ResourcFhirHandlerTestSuite) BeforeTest(suiteName string, testName string){
|
||||
func (suite *ResourcFhirHandlerTestSuite) BeforeTest(suiteName string, testName string) {
|
||||
suite.MockCtrl = gomock.NewController(suite.T())
|
||||
|
||||
|
||||
// ioutils is deprecated, used os.CreateTemp
|
||||
dbFile, err := os.CreateTemp("",fmt.Sprintf("%s.*.db",suiteName))
|
||||
if err!=nil {
|
||||
dbFile, err := os.CreateTemp("", fmt.Sprintf("%s.*.db", suiteName))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
suite.TestDatabase = dbFile
|
||||
|
||||
appConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
appConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
appConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
appConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
appConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
appConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
suite.AppConfig = appConfig
|
||||
|
||||
appRepo, err := database.NewRepository(suite.AppConfig,logrus.WithField("test",suite.T().Name()),event_bus.NewNoopEventBusServer())
|
||||
appRepo, err := database.NewRepository(suite.AppConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||
suite.AppRepository = appRepo
|
||||
|
||||
suite.AppEventBus = event_bus.NewNoopEventBusServer()
|
||||
appRepo.CreateUser(context.Background(),&models.User{
|
||||
appRepo.CreateUser(context.Background(), &models.User{
|
||||
Username: "test_user",
|
||||
Password: "test",
|
||||
})
|
||||
|
||||
|
||||
//Pre adding the source data
|
||||
w := httptest.NewRecorder()
|
||||
ctx, _ := gin.CreateTestContext(w)
|
||||
|
@ -76,8 +76,8 @@ func (suite *ResourcFhirHandlerTestSuite) BeforeTest(suiteName string, testName
|
|||
ctx.Request = req
|
||||
|
||||
CreateManualSource(ctx)
|
||||
//assert
|
||||
require.Equal(suite.T(), http.StatusOK, w.Code)
|
||||
//assert
|
||||
require.Equal(suite.T(), http.StatusOK, w.Code)
|
||||
type ResponseWrapper struct {
|
||||
Success bool `json:"success"`
|
||||
Source models.SourceCredential `json:"source"`
|
||||
|
@ -86,11 +86,10 @@ func (suite *ResourcFhirHandlerTestSuite) BeforeTest(suiteName string, testName
|
|||
var respWrapper ResponseWrapper
|
||||
err = json.Unmarshal(w.Body.Bytes(), &respWrapper)
|
||||
require.NoError(suite.T(), err)
|
||||
|
||||
|
||||
suite.SourceId = respWrapper.Source.ID
|
||||
}
|
||||
|
||||
|
||||
// AfterTest has a function to be executed right after the test finishes and receives the suite and test names as input
|
||||
func (suite *ResourcFhirHandlerTestSuite) AfterTest(suiteName, testName string) {
|
||||
suite.MockCtrl.Finish()
|
||||
|
@ -112,21 +111,21 @@ func (suite *ResourcFhirHandlerTestSuite) TestGetResourceFhirHandler() {
|
|||
|
||||
ctx.AddParam("sourceId", suite.SourceId.String())
|
||||
ctx.AddParam("resourceId", "57959813-8cd2-4e3c-8970-e4364b74980a")
|
||||
|
||||
|
||||
GetResourceFhir(ctx)
|
||||
|
||||
type ResponseWrapper struct {
|
||||
Data *models.ResourceBase `json:"data"`
|
||||
Success bool `json:"success"`
|
||||
Data *models.ResourceBase `json:"data"`
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
var respWrapper ResponseWrapper
|
||||
err := json.Unmarshal(w.Body.Bytes(), &respWrapper)
|
||||
require.NoError(suite.T(),err)
|
||||
require.Equal(suite.T(),true,respWrapper.Success)
|
||||
require.Equal(suite.T(),"Patient",respWrapper.Data.SourceResourceType)
|
||||
require.Equal(suite.T(),suite.SourceId, respWrapper.Data.SourceID)
|
||||
require.Equal(suite.T(),"57959813-8cd2-4e3c-8970-e4364b74980a", respWrapper.Data.SourceResourceID)
|
||||
|
||||
require.NoError(suite.T(), err)
|
||||
require.Equal(suite.T(), true, respWrapper.Success)
|
||||
require.Equal(suite.T(), "Patient", respWrapper.Data.SourceResourceType)
|
||||
require.Equal(suite.T(), suite.SourceId, respWrapper.Data.SourceID)
|
||||
require.Equal(suite.T(), "57959813-8cd2-4e3c-8970-e4364b74980a", respWrapper.Data.SourceResourceID)
|
||||
|
||||
}
|
||||
|
||||
func (suite *ResourcFhirHandlerTestSuite) TestGetResourceFhirHandler_WithInvalidSourceResourceId() {
|
||||
|
@ -140,18 +139,18 @@ func (suite *ResourcFhirHandlerTestSuite) TestGetResourceFhirHandler_WithInvalid
|
|||
|
||||
ctx.AddParam("sourceId", "-1")
|
||||
ctx.AddParam("resourceId", "57959813-9999-4e3c-8970-e4364b74980a")
|
||||
|
||||
|
||||
GetResourceFhir(ctx)
|
||||
|
||||
type ResponseWrapper struct {
|
||||
Data *models.ResourceBase `json:"data"`
|
||||
Success bool `json:"success"`
|
||||
Data *models.ResourceBase `json:"data"`
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
var respWrapper ResponseWrapper
|
||||
err := json.Unmarshal(w.Body.Bytes(), &respWrapper)
|
||||
require.NoError(suite.T(),err)
|
||||
|
||||
require.Equal(suite.T(),false,respWrapper.Success)
|
||||
require.Nil(suite.T(),respWrapper.Data)
|
||||
|
||||
}
|
||||
require.NoError(suite.T(), err)
|
||||
|
||||
require.Equal(suite.T(), false, respWrapper.Success)
|
||||
require.Nil(suite.T(), respWrapper.Data)
|
||||
|
||||
}
|
||||
|
|
|
@ -55,6 +55,7 @@ func (suite *SourceHandlerTestSuite) BeforeTest(suiteName, testName string) {
|
|||
appConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||
appConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||
appConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||
appConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||
appConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||
suite.AppConfig = appConfig
|
||||
|
||||
|
|
15
go.mod
15
go.mod
|
@ -1,9 +1,13 @@
|
|||
module github.com/fastenhealth/fasten-onprem
|
||||
|
||||
go 1.18
|
||||
|
||||
//replace github.com/fastenhealth/fasten-sources => ../fasten-sources
|
||||
//replace github.com/fastenhealth/gofhir-models => ../gofhir-models
|
||||
|
||||
go 1.18
|
||||
replace github.com/mattn/go-sqlite3 v1.14.17 => github.com/jgiannuzzi/go-sqlite3 v1.14.17-0.20230719111531-6e53453ccbd3
|
||||
|
||||
//replace gorm.io/driver/sqlite v1.5.4 => github.com/jgiannuzzi/gorm-sqlite v1.4.4-0.20221215225833-42389ad31305
|
||||
|
||||
require (
|
||||
github.com/analogj/go-util v0.0.0-20210417161720-39b497cca03b
|
||||
|
@ -13,7 +17,6 @@ require (
|
|||
github.com/fastenhealth/fasten-sources v0.4.9
|
||||
github.com/fastenhealth/gofhir-models v0.0.6
|
||||
github.com/gin-gonic/gin v1.9.0
|
||||
github.com/glebarez/sqlite v1.5.0
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.1.1
|
||||
github.com/golang-jwt/jwt/v4 v4.4.2
|
||||
github.com/golang/mock v1.6.0
|
||||
|
@ -30,6 +33,7 @@ require (
|
|||
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17
|
||||
golang.org/x/net v0.14.0
|
||||
gorm.io/datatypes v1.0.7
|
||||
gorm.io/driver/sqlite v1.5.4
|
||||
gorm.io/gorm v1.25.4
|
||||
)
|
||||
|
||||
|
@ -37,6 +41,7 @@ require (
|
|||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/jackc/pgx/v5 v5.4.3 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.17 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
|
@ -51,7 +56,6 @@ require (
|
|||
github.com/fatih/color v1.13.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/glebarez/go-sqlite v1.19.1 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.13.0 // indirect
|
||||
|
@ -83,7 +87,6 @@ require (
|
|||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.7 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/seborama/govcr v4.5.0+incompatible // indirect
|
||||
github.com/segmentio/asm v1.2.0 // indirect
|
||||
|
@ -107,8 +110,4 @@ require (
|
|||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gorm.io/driver/mysql v1.3.2 // indirect
|
||||
gorm.io/driver/postgres v1.5.3
|
||||
modernc.org/libc v1.19.0 // indirect
|
||||
modernc.org/mathutil v1.5.0 // indirect
|
||||
modernc.org/memory v1.4.0 // indirect
|
||||
modernc.org/sqlite v1.19.1 // indirect
|
||||
)
|
||||
|
|
52
go.sum
52
go.sum
|
@ -95,7 +95,6 @@ github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3 h1:+3HCtB74++ClLy8GgjU
|
|||
github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4=
|
||||
github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
|
||||
github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
|
@ -116,10 +115,6 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE
|
|||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8=
|
||||
github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k=
|
||||
github.com/glebarez/go-sqlite v1.19.1 h1:o2XhjyR8CQ2m84+bVz10G0cabmG0tY4sIMiCbrcUTrY=
|
||||
github.com/glebarez/go-sqlite v1.19.1/go.mod h1:9AykawGIyIcxoSfpYWiX1SgTNHTNsa/FVc75cDkbp4M=
|
||||
github.com/glebarez/sqlite v1.5.0 h1:+8LAEpmywqresSoGlqjjT+I9m4PseIM3NcerIJ/V7mk=
|
||||
github.com/glebarez/sqlite v1.5.0/go.mod h1:0wzXzTvfVJIN2GqRhCdMbnYd+m+aH5/QV7B30rM6NgY=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
|
@ -189,7 +184,6 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
|
@ -272,6 +266,8 @@ github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0f
|
|||
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jgiannuzzi/go-sqlite3 v1.14.17-0.20230719111531-6e53453ccbd3 h1:Iqr8ZWwijosAcawoD9IZ7cwj61WH50Rysm+RQ+ZIB4I=
|
||||
github.com/jgiannuzzi/go-sqlite3 v1.14.17-0.20230719111531-6e53453ccbd3/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
|
@ -281,7 +277,6 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
|
|||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
|
||||
|
@ -332,13 +327,10 @@ github.com/mattn/go-isatty v0.0.6/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
|
|||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
|
||||
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
||||
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
|
@ -360,8 +352,6 @@ github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qR
|
|||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
|
@ -623,7 +613,6 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
@ -699,7 +688,6 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc
|
|||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
|
@ -830,13 +818,13 @@ gorm.io/driver/mysql v1.3.2/go.mod h1:ChK6AHbHgDCFZyJp0F+BmVGb06PSIoh9uVYKAlRbb2
|
|||
gorm.io/driver/postgres v1.3.4/go.mod h1:y0vEuInFKJtijuSGu9e5bs5hzzSzPK+LancpKpvbRBw=
|
||||
gorm.io/driver/postgres v1.5.3 h1:qKGY5CPHOuj47K/VxbCXJfFvIUeqMSXXadqdCY+MbBU=
|
||||
gorm.io/driver/postgres v1.5.3/go.mod h1:F+LtvlFhZT7UBiA81mC9W6Su3D4WUhSboc/36QZU0gk=
|
||||
gorm.io/driver/sqlite v1.3.1 h1:bwfE+zTEWklBYoEodIOIBwuWHpnx52Z9zJFW5F33WLk=
|
||||
gorm.io/driver/sqlite v1.3.1/go.mod h1:wJx0hJspfycZ6myN38x1O/AqLtNS6c5o9TndewFbELg=
|
||||
gorm.io/driver/sqlite v1.5.4 h1:IqXwXi8M/ZlPzH/947tn5uik3aYQslP9BVveoax0nV0=
|
||||
gorm.io/driver/sqlite v1.5.4/go.mod h1:qxAuCol+2r6PannQDpOP1FP6ag3mKi4esLnB/jHed+4=
|
||||
gorm.io/driver/sqlserver v1.3.1 h1:F5t6ScMzOgy1zukRTIZgLZwKahgt3q1woAILVolKpOI=
|
||||
gorm.io/driver/sqlserver v1.3.1/go.mod h1:w25Vrx2BG+CJNUu/xKbFhaKlGxT/nzRkhWCCoptX8tQ=
|
||||
gorm.io/gorm v1.23.1/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||
gorm.io/gorm v1.23.6/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||
gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||
gorm.io/gorm v1.25.4 h1:iyNd8fNAe8W9dvtlgeRI5zSVZPsq3OpcTu37cYcpCmw=
|
||||
gorm.io/gorm v1.25.4/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
@ -846,38 +834,6 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
|
|||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||
modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
|
||||
modernc.org/cc/v3 v3.37.0/go.mod h1:vtL+3mdHx/wcj3iEGz84rQa8vEqR6XM84v5Lcvfph20=
|
||||
modernc.org/cc/v3 v3.38.1/go.mod h1:vtL+3mdHx/wcj3iEGz84rQa8vEqR6XM84v5Lcvfph20=
|
||||
modernc.org/ccgo/v3 v3.0.0-20220904174949-82d86e1b6d56/go.mod h1:YSXjPL62P2AMSxBphRHPn7IkzhVHqkvOnRKAKh+W6ZI=
|
||||
modernc.org/ccgo/v3 v3.0.0-20220910160915-348f15de615a/go.mod h1:8p47QxPkdugex9J4n9P2tLZ9bK01yngIVp00g4nomW0=
|
||||
modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo=
|
||||
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
|
||||
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
|
||||
modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0=
|
||||
modernc.org/libc v1.17.4/go.mod h1:WNg2ZH56rDEwdropAJeZPQkXmDwh+JCA1s/htl6r2fA=
|
||||
modernc.org/libc v1.18.0/go.mod h1:vj6zehR5bfc98ipowQOM2nIDUZnVew/wNC/2tOGS+q0=
|
||||
modernc.org/libc v1.19.0 h1:bXyVhGQg6KIClTr8FMVIDPl7jtbcs7aS5WP7vLDaxPs=
|
||||
modernc.org/libc v1.19.0/go.mod h1:ZRfIaEkgrYgZDl6pa4W39HgN5G/yDW+NRmNKZBDFrk0=
|
||||
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
||||
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
|
||||
modernc.org/memory v1.3.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||
modernc.org/memory v1.4.0 h1:crykUfNSnMAXaOJnnxcSzbUGMqkLWjklJKkBK2nwZwk=
|
||||
modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/sqlite v1.19.1 h1:8xmS5oLnZtAK//vnd4aTVj8VOeTAccEFOtUnIzfSw+4=
|
||||
modernc.org/sqlite v1.19.1/go.mod h1:UfQ83woKMaPW/ZBruK0T7YaFCrI+IE0LeWVY6pmnVms=
|
||||
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
|
||||
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
|
||||
modernc.org/tcl v1.14.0/go.mod h1:gQ7c1YPMvryCHCcmf8acB6VPabE59QBeuRQLL7cTUlM=
|
||||
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
modernc.org/z v1.6.0/go.mod h1:hVdgNMh8ggTuRG1rGU8x+xGRFfiQUIAw0ZqlPy8+HyQ=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
|
|
Loading…
Reference in New Issue