Merge pull request #275 from fastenhealth/nickmurray/add-postgres-db-option

This commit is contained in:
Jason Kulatunga 2023-10-16 11:43:36 -07:00 committed by GitHub
commit 8861e4b6ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 1469 additions and 1245 deletions

View File

@ -30,6 +30,7 @@ func (c *configuration) Init() error {
c.SetDefault("web.allow_unsafe_endpoints", false)
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("cache.location", "/opt/fasten/cache/")

View File

@ -5,6 +5,8 @@ type BackgroundJobStatus string
type BackgroundJobType string
type BackgroundJobSchedule string
type DatabaseRepositoryType string
const (
ResourceListPageSize int = 20
@ -36,4 +38,7 @@ const (
BackgroundJobScheduleWeekly BackgroundJobSchedule = "WEEKLY"
BackgroundJobScheduleBiWeekly BackgroundJobSchedule = "BIWEEKLY"
BackgroundJobScheduleMonthly BackgroundJobSchedule = "MONTHLY"
DatabaseRepositoryTypeSqlite DatabaseRepositoryType = "sqlite"
DatabaseRepositoryTypePostgres DatabaseRepositoryType = "postgres"
)

View File

@ -0,0 +1,20 @@
package database
import (
"github.com/fastenhealth/fasten-onprem/backend/pkg"
"github.com/fastenhealth/fasten-onprem/backend/pkg/config"
"github.com/fastenhealth/fasten-onprem/backend/pkg/errors"
"github.com/fastenhealth/fasten-onprem/backend/pkg/event_bus"
"github.com/sirupsen/logrus"
)
func NewRepository(appConfig config.Interface, globalLogger logrus.FieldLogger, eventBus event_bus.Interface) (DatabaseRepository, error) {
switch pkg.DatabaseRepositoryType(appConfig.GetString("database.type")) {
case pkg.DatabaseRepositoryTypeSqlite:
return newSqliteRepository(appConfig, globalLogger, eventBus)
case pkg.DatabaseRepositoryTypePostgres:
return newPostgresRepository(appConfig, globalLogger, eventBus)
default:
return nil, errors.DatabaseTypeNotSupportedError(appConfig.GetString("database.type"))
}
}

File diff suppressed because it is too large Load Diff

View File

@ -3,14 +3,15 @@ package database
import (
"context"
"fmt"
"log"
"strings"
"github.com/dominikbraun/graph"
"github.com/fastenhealth/fasten-onprem/backend/pkg"
"github.com/fastenhealth/fasten-onprem/backend/pkg/models"
"github.com/fastenhealth/fasten-onprem/backend/pkg/utils"
databaseModel "github.com/fastenhealth/fasten-onprem/backend/pkg/models/database"
"github.com/fastenhealth/fasten-onprem/backend/pkg/utils"
"golang.org/x/exp/slices"
"log"
"strings"
)
type VertexResourcePlaceholder struct {
@ -28,8 +29,8 @@ func (rp *VertexResourcePlaceholder) ID() string {
// Retrieve a list of all fhir resources (vertex), and a list of all associations (edge)
// Generate a graph
// return list of root nodes, and their flattened related resources.
func (sr *SqliteRepository) GetFlattenedResourceGraph(ctx context.Context, graphType pkg.ResourceGraphType, options models.ResourceGraphOptions) (map[string][]*models.ResourceBase, *models.ResourceGraphMetadata, error) {
currentUser, currentUserErr := sr.GetCurrentUser(ctx)
func (gr *GormRepository) GetFlattenedResourceGraph(ctx context.Context, graphType pkg.ResourceGraphType, options models.ResourceGraphOptions) (map[string][]*models.ResourceBase, *models.ResourceGraphMetadata, error) {
currentUser, currentUserErr := gr.GetCurrentUser(ctx)
if currentUserErr != nil {
return nil, nil, currentUserErr
}
@ -45,7 +46,7 @@ func (sr *SqliteRepository) GetFlattenedResourceGraph(ctx context.Context, graph
var relatedResourceRelationships []models.RelatedResource
// SELECT * FROM related_resources WHERE user_id = "53c1e930-63af-46c9-b760-8e83cbc1abd9";
result := sr.GormClient.WithContext(ctx).
result := gr.GormClient.WithContext(ctx).
Where(models.RelatedResource{
ResourceBaseUserID: currentUser.ID,
}).
@ -59,7 +60,7 @@ func (sr *SqliteRepository) GetFlattenedResourceGraph(ctx context.Context, graph
g := graph.New(resourceVertexId, graph.Directed(), graph.Acyclic(), graph.Rooted())
//// Get list of all resources TODO - REPLACED THIS
//wrappedResourceModels, err := sr.ListResources(ctx, models.ListResourceQueryOptions{})
//wrappedResourceModels, err := gr.ListResources(ctx, models.ListResourceQueryOptions{})
//if err != nil {
// return nil, err
//}
@ -109,7 +110,7 @@ func (sr *SqliteRepository) GetFlattenedResourceGraph(ctx context.Context, graph
}
//add recriprocial relationships (depending on the graph type)
relatedResourceRelationships = sr.PopulateGraphTypeReciprocalRelationships(graphType, relatedResourceRelationships)
relatedResourceRelationships = gr.PopulateGraphTypeReciprocalRelationships(graphType, relatedResourceRelationships)
//add edges to graph
for _, relationship := range relatedResourceRelationships {
@ -121,7 +122,7 @@ func (sr *SqliteRepository) GetFlattenedResourceGraph(ctx context.Context, graph
if err != nil {
//this may occur because vertices may not exist
sr.Logger.Warnf("ignoring, an error occurred while adding edge: %v", err)
gr.Logger.Warnf("ignoring, an error occurred while adding edge: %v", err)
}
}
@ -200,7 +201,7 @@ func (sr *SqliteRepository) GetFlattenedResourceGraph(ctx context.Context, graph
// Step 2: now that we've created a relationship graph using placeholders, we need to determine which page of resources to return
// and look up the actual resources from the database.
resourceListDictionary, totalElements, err := sr.InflateResourceGraphAtPage(resourcePlaceholderListDictionary, options.Page)
resourceListDictionary, totalElements, err := gr.InflateResourceGraphAtPage(resourcePlaceholderListDictionary, options.Page)
if err != nil {
return nil, nil, fmt.Errorf("error while paginating & inflating resource graph: %v", err)
}
@ -215,7 +216,7 @@ func (sr *SqliteRepository) GetFlattenedResourceGraph(ctx context.Context, graph
SourceID: resource.SourceID.String(),
UserID: resource.UserID.String(),
})
sr.Logger.Debugf("populating resourcePlaceholder: %s", vertexId)
gr.Logger.Debugf("populating resourcePlaceholder: %s", vertexId)
resource.RelatedResource = []*models.ResourceBase{}
@ -226,9 +227,9 @@ func (sr *SqliteRepository) GetFlattenedResourceGraph(ctx context.Context, graph
//skip the current resourcePlaceholder if it's referenced in this list.
//also skip the current resourcePlaceholder if its a Binary resourcePlaceholder (which is a special case)
if vertexId != resourceVertexId(relatedResourcePlaceholder) && relatedResourcePlaceholder.ResourceType != "Binary" {
relatedResource, err := sr.GetResourceByResourceTypeAndId(ctx, relatedResourcePlaceholder.ResourceType, relatedResourcePlaceholder.ResourceID)
relatedResource, err := gr.GetResourceByResourceTypeAndId(ctx, relatedResourcePlaceholder.ResourceType, relatedResourcePlaceholder.ResourceID)
if err != nil {
sr.Logger.Warnf("ignoring, cannot safely handle error which occurred while getting related resource: %v", err)
gr.Logger.Warnf("ignoring, cannot safely handle error which occurred while getting related resource: %v", err)
return true
}
resource.RelatedResource = append(
@ -268,9 +269,9 @@ func (sr *SqliteRepository) GetFlattenedResourceGraph(ctx context.Context, graph
vertexId := resourceKeysVertexId(currentResource.SourceID.String(), currentResource.SourceResourceType, currentResource.SourceResourceID)
for relatedVertexId, _ := range adjacencyMap[vertexId] {
relatedResourcePlaceholder, _ := g.Vertex(relatedVertexId)
relatedResourceFhir, err := sr.GetResourceByResourceTypeAndId(ctx, relatedResourcePlaceholder.ResourceType, relatedResourcePlaceholder.ResourceID)
relatedResourceFhir, err := gr.GetResourceByResourceTypeAndId(ctx, relatedResourcePlaceholder.ResourceType, relatedResourcePlaceholder.ResourceID)
if err != nil {
sr.Logger.Warnf("ignoring, cannot safely handle error which occurred while getting related resource (flatten=false): %v", err)
gr.Logger.Warnf("ignoring, cannot safely handle error which occurred while getting related resource (flatten=false): %v", err)
continue
}
flattenRelatedResourcesFn(relatedResourceFhir)
@ -295,7 +296,7 @@ func (sr *SqliteRepository) GetFlattenedResourceGraph(ctx context.Context, graph
// - sort the root resources by date, desc
// - use the page number + page size to determine which root resources to return
// - return a dictionary of "source" resource lists
func (sr *SqliteRepository) InflateResourceGraphAtPage(resourcePlaceholderListDictionary map[string][]*VertexResourcePlaceholder, page int) (map[string][]*models.ResourceBase, int, error) {
func (gr *GormRepository) InflateResourceGraphAtPage(resourcePlaceholderListDictionary map[string][]*VertexResourcePlaceholder, page int) (map[string][]*models.ResourceBase, int, error) {
totalElements := 0
// Step 3a: since we cant calulate the sort order until the resources are loaded, we need to load all the root resources first.
@ -319,7 +320,7 @@ func (sr *SqliteRepository) InflateResourceGraphAtPage(resourcePlaceholderListDi
return nil, totalElements, err
}
var tableWrappedResourceModels []models.ResourceBase
sr.GormClient.
gr.GormClient.
Where("(user_id, source_id, source_resource_type, source_resource_id) IN ?", selectList).
Table(tableName).
Find(&tableWrappedResourceModels)
@ -351,15 +352,15 @@ func (sr *SqliteRepository) InflateResourceGraphAtPage(resourcePlaceholderListDi
return resourceListDictionary, totalElements, nil
}
//We need to support the following types of graphs:
// We need to support the following types of graphs:
// - Medical History
// - AddressBook (contacts)
// - Medications
// - Billing Report
//edges are always "strongly connected", however "source" nodes (roots, like Condition or Encounter -- depending on ) are only one way.
//add an edge from every resource to its related resource. Keep in mind that FHIR resources may not contain reciprocal edges, so we ensure the graph is rooted by flipping any
//related resources that are "Condition" or "Encounter"
func (sr *SqliteRepository) PopulateGraphTypeReciprocalRelationships(graphType pkg.ResourceGraphType, relationships []models.RelatedResource) []models.RelatedResource {
// edges are always "strongly connected", however "source" nodes (roots, like Condition or Encounter -- depending on ) are only one way.
// add an edge from every resource to its related resource. Keep in mind that FHIR resources may not contain reciprocal edges, so we ensure the graph is rooted by flipping any
// related resources that are "Condition" or "Encounter"
func (gr *GormRepository) PopulateGraphTypeReciprocalRelationships(graphType pkg.ResourceGraphType, relationships []models.RelatedResource) []models.RelatedResource {
reciprocalRelationships := []models.RelatedResource{}
//prioritized lists of sources and sinks for the graph. We will use these to determine which resources are "root" nodes.
@ -469,7 +470,7 @@ func getSourcesAndSinksForGraphType(graphType pkg.ResourceGraphType) ([][]string
return sources, sinks, sourceFlattenRelated
}
//source resource types are resources that are at the root of the graph, nothing may reference them directly
// source resource types are resources that are at the root of the graph, nothing may reference them directly
// loop though the list of source resource types, and see if the checkResourceType is one of them
func foundResourceGraphSource(checkResourceType string, sourceResourceTypes [][]string) int {
found := -1
@ -482,7 +483,7 @@ func foundResourceGraphSource(checkResourceType string, sourceResourceTypes [][]
return found
}
//sink resource types are the leaves of the graph, they must not reference anything else. (only be referenced)
// sink resource types are the leaves of the graph, they must not reference anything else. (only be referenced)
func foundResourceGraphSink(checkResourceType string, sinkResourceTypes [][]string) int {
found := -1
for i, sinkResourceType := range sinkResourceTypes {

View File

@ -3,6 +3,10 @@ package database
import (
"context"
"fmt"
"strconv"
"strings"
"time"
"github.com/fastenhealth/fasten-onprem/backend/pkg/models"
databaseModel "github.com/fastenhealth/fasten-onprem/backend/pkg/models/database"
"github.com/iancoleman/strcase"
@ -10,9 +14,6 @@ import (
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
"gorm.io/gorm"
"strconv"
"strings"
"time"
)
type SearchParameterType string
@ -52,9 +53,9 @@ const TABLE_ALIAS = "fhir"
// )
// AND (user_id = "6efcd7c5-3f29-4f0d-926d-a66ff68bbfc2")
// GROUP BY `fhir`.`id`
func (sr *SqliteRepository) QueryResources(ctx context.Context, query models.QueryResource) (interface{}, error) {
func (gr *GormRepository) QueryResources(ctx context.Context, query models.QueryResource) (interface{}, error) {
sqlQuery, err := sr.sqlQueryResources(ctx, query)
sqlQuery, err := gr.sqlQueryResources(ctx, query)
if err != nil {
return nil, err
}
@ -74,7 +75,7 @@ func (sr *SqliteRepository) QueryResources(ctx context.Context, query models.Que
// see QueryResources
// this function has all the logic, but should only be called directly for testing
func (sr *SqliteRepository) sqlQueryResources(ctx context.Context, query models.QueryResource) (*gorm.DB, error) {
func (gr *GormRepository) sqlQueryResources(ctx context.Context, query models.QueryResource) (*gorm.DB, error) {
//todo, until we actually parse the select statement, we will just return all resources based on "from"
//SECURITY: this is required to ensure that only valid resource types are queried (since it's controlled by the user)
@ -134,7 +135,7 @@ func (sr *SqliteRepository) sqlQueryResources(ctx context.Context, query models.
}
//SECURITY: for safety, we will always add/override the current user_id to the where clause. This is to ensure that the user doesnt attempt to override this value in their own where clause
currentUser, currentUserErr := sr.GetCurrentUser(ctx)
currentUser, currentUserErr := gr.GetCurrentUser(ctx)
if currentUserErr != nil {
return nil, currentUserErr
}
@ -234,7 +235,7 @@ func (sr *SqliteRepository) sqlQueryResources(ctx context.Context, query models.
fromClauses = lo.Uniq(fromClauses)
fromClauses = lo.Compact(fromClauses)
sqlQuery := sr.GormClient.WithContext(ctx).
sqlQuery := gr.GormClient.WithContext(ctx).
Select(strings.Join(selectClauses, ", ")).
Where(strings.Join(whereClauses, " AND "), whereNamedParameters).
Group(groupClause).
@ -253,7 +254,7 @@ func (sr *SqliteRepository) sqlQueryResources(ctx context.Context, query models.
}
/// INTERNAL functionality. These functions are exported for testing, but are not available in the Interface
//TODO: dont export these, instead use casting to convert the interface to the SqliteRepository struct, then call ehese functions directly
//TODO: dont export these, instead use casting to convert the interface to the GormRepository struct, then call ehese functions directly
type SearchParameter struct {
Name string

View File

@ -3,6 +3,12 @@ package database
import (
"context"
"fmt"
"io/ioutil"
"log"
"os"
"strings"
"testing"
"github.com/fastenhealth/fasten-onprem/backend/pkg"
mock_config "github.com/fastenhealth/fasten-onprem/backend/pkg/config/mock"
"github.com/fastenhealth/fasten-onprem/backend/pkg/event_bus"
@ -12,11 +18,6 @@ import (
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"gorm.io/gorm"
"io/ioutil"
"log"
"os"
"strings"
"testing"
)
// Define the suite, and absorb the built-in basic suite
@ -42,6 +43,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().GetString("log.level").Return("INFO").AnyTimes()
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)
@ -71,7 +73,7 @@ func TestRepositorySqlTestSuite(t *testing.T) {
func (suite *RepositorySqlTestSuite) TestQueryResources_SQL() {
//setup
sqliteRepo := suite.TestRepository.(*SqliteRepository)
sqliteRepo := suite.TestRepository.(*GormRepository)
sqliteRepo.GormClient = sqliteRepo.GormClient.Session(&gorm.Session{DryRun: true})
//test
@ -108,7 +110,7 @@ func (suite *RepositorySqlTestSuite) TestQueryResources_SQL() {
func (suite *RepositorySqlTestSuite) TestQueryResources_SQL_WithMultipleWhereConditions() {
//setup
sqliteRepo := suite.TestRepository.(*SqliteRepository)
sqliteRepo := suite.TestRepository.(*GormRepository)
sqliteRepo.GormClient = sqliteRepo.GormClient.Session(&gorm.Session{DryRun: true})
//test
@ -146,7 +148,7 @@ func (suite *RepositorySqlTestSuite) TestQueryResources_SQL_WithMultipleWhereCon
func (suite *RepositorySqlTestSuite) TestQueryResources_SQL_WithPrimitiveOrderByAggregation() {
//setup
sqliteRepo := suite.TestRepository.(*SqliteRepository)
sqliteRepo := suite.TestRepository.(*GormRepository)
sqliteRepo.GormClient = sqliteRepo.GormClient.Session(&gorm.Session{DryRun: true})
//test
@ -183,7 +185,7 @@ func (suite *RepositorySqlTestSuite) TestQueryResources_SQL_WithPrimitiveOrderBy
func (suite *RepositorySqlTestSuite) TestQueryResources_SQL_WithKeywordOrderByAggregation() {
//setup
sqliteRepo := suite.TestRepository.(*SqliteRepository)
sqliteRepo := suite.TestRepository.(*GormRepository)
sqliteRepo.GormClient = sqliteRepo.GormClient.Session(&gorm.Session{DryRun: true})
//test
@ -218,7 +220,7 @@ func (suite *RepositorySqlTestSuite) TestQueryResources_SQL_WithKeywordOrderByAg
func (suite *RepositorySqlTestSuite) TestQueryResources_SQL_WithComplexOrderByAggregation() {
//setup
sqliteRepo := suite.TestRepository.(*SqliteRepository)
sqliteRepo := suite.TestRepository.(*GormRepository)
sqliteRepo.GormClient = sqliteRepo.GormClient.Session(&gorm.Session{DryRun: true})
//test
@ -255,7 +257,7 @@ func (suite *RepositorySqlTestSuite) TestQueryResources_SQL_WithComplexOrderByAg
func (suite *RepositorySqlTestSuite) TestQueryResources_SQL_WithPrimitiveCountByAggregation() {
//setup
sqliteRepo := suite.TestRepository.(*SqliteRepository)
sqliteRepo := suite.TestRepository.(*GormRepository)
sqliteRepo.GormClient = sqliteRepo.GormClient.Session(&gorm.Session{DryRun: true})
//test
@ -292,7 +294,7 @@ func (suite *RepositorySqlTestSuite) TestQueryResources_SQL_WithPrimitiveCountBy
func (suite *RepositorySqlTestSuite) TestQueryResources_SQL_WithKeywordCountByAggregation() {
//setup
sqliteRepo := suite.TestRepository.(*SqliteRepository)
sqliteRepo := suite.TestRepository.(*GormRepository)
sqliteRepo.GormClient = sqliteRepo.GormClient.Session(&gorm.Session{DryRun: true})
//test
@ -329,7 +331,7 @@ func (suite *RepositorySqlTestSuite) TestQueryResources_SQL_WithKeywordCountByAg
func (suite *RepositorySqlTestSuite) TestQueryResources_SQL_WithWildcardCountByAggregation() {
//setup
sqliteRepo := suite.TestRepository.(*SqliteRepository)
sqliteRepo := suite.TestRepository.(*GormRepository)
sqliteRepo.GormClient = sqliteRepo.GormClient.Session(&gorm.Session{DryRun: true})
//test
@ -364,7 +366,7 @@ func (suite *RepositorySqlTestSuite) TestQueryResources_SQL_WithWildcardCountByA
func (suite *RepositorySqlTestSuite) TestQueryResources_SQL_WithComplexCountByAggregation() {
//setup
sqliteRepo := suite.TestRepository.(*SqliteRepository)
sqliteRepo := suite.TestRepository.(*GormRepository)
sqliteRepo.GormClient = sqliteRepo.GormClient.Session(&gorm.Session{DryRun: true})
//test
@ -401,7 +403,7 @@ func (suite *RepositorySqlTestSuite) TestQueryResources_SQL_WithComplexCountByAg
func (suite *RepositorySqlTestSuite) TestQueryResources_SQL_WithComplexGroupByWithOrderByMaxFnAggregation() {
//setup
sqliteRepo := suite.TestRepository.(*SqliteRepository)
sqliteRepo := suite.TestRepository.(*GormRepository)
sqliteRepo.GormClient = sqliteRepo.GormClient.Session(&gorm.Session{DryRun: true})
//test
@ -441,7 +443,7 @@ func (suite *RepositorySqlTestSuite) TestQueryResources_SQL_WithComplexGroupByWi
func (suite *RepositorySqlTestSuite) TestQueryResources_SQL_WithTokenGroupByNoModifier() {
//setup
sqliteRepo := suite.TestRepository.(*SqliteRepository)
sqliteRepo := suite.TestRepository.(*GormRepository)
sqliteRepo.GormClient = sqliteRepo.GormClient.Session(&gorm.Session{DryRun: true})
//test
@ -478,7 +480,7 @@ func (suite *RepositorySqlTestSuite) TestQueryResources_SQL_WithTokenGroupByNoMo
func (suite *RepositorySqlTestSuite) TestQueryResources_SQL_WithTokenGroupByNoModifierWithLimit() {
//setup
sqliteRepo := suite.TestRepository.(*SqliteRepository)
sqliteRepo := suite.TestRepository.(*GormRepository)
sqliteRepo.GormClient = sqliteRepo.GormClient.Session(&gorm.Session{DryRun: true})
//test

View File

@ -2,6 +2,10 @@ package database
import (
"context"
"strings"
"testing"
"time"
"github.com/fastenhealth/fasten-onprem/backend/pkg"
mock_config "github.com/fastenhealth/fasten-onprem/backend/pkg/config/mock"
"github.com/fastenhealth/fasten-onprem/backend/pkg/event_bus"
@ -9,9 +13,6 @@ import (
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/require"
"gorm.io/gorm"
"strings"
"testing"
"time"
)
// mimic tests from https://hl7.org/fhir/r4/search.html#token
@ -259,6 +260,7 @@ func (suite *RepositoryTestSuite) TestQueryResources_SQL() {
//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().GetString("log.level").Return("INFO").AnyTimes()
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)
@ -270,7 +272,7 @@ func (suite *RepositoryTestSuite) TestQueryResources_SQL() {
err = dbRepo.CreateUser(context.Background(), userModel)
require.NoError(suite.T(), err)
sqliteRepo := dbRepo.(*SqliteRepository)
sqliteRepo := dbRepo.(*GormRepository)
sqliteRepo.GormClient = sqliteRepo.GormClient.Session(&gorm.Session{DryRun: true})
//test

View File

@ -3,19 +3,20 @@ package database
import (
"context"
"fmt"
"github.com/fastenhealth/fasten-onprem/backend/pkg/models"
"github.com/google/uuid"
)
// LoadSettings will retrieve settings from the database, store them in the AppConfig object, and return a Settings struct
func (sr *SqliteRepository) LoadUserSettings(ctx context.Context) (*models.UserSettings, error) {
currentUser, currentUserErr := sr.GetCurrentUser(ctx)
func (gr *GormRepository) LoadUserSettings(ctx context.Context) (*models.UserSettings, error) {
currentUser, currentUserErr := gr.GetCurrentUser(ctx)
if currentUserErr != nil {
return nil, currentUserErr
}
settingsEntries := []models.UserSettingEntry{}
if err := sr.GormClient.
if err := gr.GormClient.
WithContext(ctx).
Where(models.UserSettingEntry{
UserID: currentUser.ID,
@ -38,8 +39,8 @@ func (sr *SqliteRepository) LoadUserSettings(ctx context.Context) (*models.UserS
// testing
// curl -d '{"metrics": { "notify_level": 5, "status_filter_attributes": 5, "status_threshold": 5 }}' -H "Content-Type: application/json" -X POST http://localhost:9090/api/settings
// SaveSettings will update settings in AppConfig object, then save the settings to the database.
func (sr *SqliteRepository) SaveUserSettings(ctx context.Context, newSettings *models.UserSettings) error {
currentUser, currentUserErr := sr.GetCurrentUser(ctx)
func (gr *GormRepository) SaveUserSettings(ctx context.Context, newSettings *models.UserSettings) error {
currentUser, currentUserErr := gr.GetCurrentUser(ctx)
if currentUserErr != nil {
return currentUserErr
}
@ -47,7 +48,7 @@ func (sr *SqliteRepository) SaveUserSettings(ctx context.Context, newSettings *m
//retrieve current settings from the database
currentSettingsEntries := []models.UserSettingEntry{}
if err := sr.GormClient.
if err := gr.GormClient.
WithContext(ctx).
Where(models.UserSettingEntry{
UserID: currentUser.ID,
@ -66,8 +67,8 @@ func (sr *SqliteRepository) SaveUserSettings(ctx context.Context, newSettings *m
for ndx, settingsEntry := range newSettingsEntries {
// store in database.
//TODO: this should be `sr.gormClient.Updates(&settingsEntries).Error`
err := sr.GormClient.
//TODO: this should be `gr.gormClient.Updates(&settingsEntries).Error`
err := gr.GormClient.
WithContext(ctx).
Model(&models.UserSettingEntry{}).
Where([]uuid.UUID{settingsEntry.ID}).
@ -80,7 +81,7 @@ func (sr *SqliteRepository) SaveUserSettings(ctx context.Context, newSettings *m
return nil
}
func (sr *SqliteRepository) PopulateDefaultUserSettings(ctx context.Context, userId uuid.UUID) error {
func (gr *GormRepository) PopulateDefaultUserSettings(ctx context.Context, userId uuid.UUID) error {
//retrieve current settings from the database
settingsEntries := []models.UserSettingEntry{}
@ -92,6 +93,6 @@ func (sr *SqliteRepository) PopulateDefaultUserSettings(ctx context.Context, use
SettingValueArray: []string{},
})
return sr.GormClient.WithContext(ctx).Create(settingsEntries).Error
return gr.GormClient.WithContext(ctx).Create(settingsEntries).Error
}

View File

@ -3,6 +3,13 @@ package database
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http/httptest"
"os"
"testing"
"time"
"github.com/fastenhealth/fasten-onprem/backend/pkg"
mock_config "github.com/fastenhealth/fasten-onprem/backend/pkg/config/mock"
"github.com/fastenhealth/fasten-onprem/backend/pkg/event_bus"
@ -18,18 +25,12 @@ import (
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"golang.org/x/net/context"
"io/ioutil"
"log"
"net/http/httptest"
"os"
"testing"
"time"
)
func TestSourceCredentialInterface(t *testing.T) {
t.Parallel()
repo := new(SqliteRepository)
repo := new(GormRepository)
//assert
require.Implements(t, (*sourceModels.DatabaseRepository)(nil), repo, "should implement the DatabaseRepository interface from fasten-sources")
@ -74,6 +75,7 @@ func (suite *RepositoryTestSuite) TestNewRepository() {
//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().GetString("log.level").Return("INFO").AnyTimes()
//test
@ -87,6 +89,7 @@ func (suite *RepositoryTestSuite) TestCreateUser() {
//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().GetString("log.level").Return("INFO").AnyTimes()
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)
@ -107,6 +110,7 @@ func (suite *RepositoryTestSuite) TestCreateUser_WithExitingUser_ShouldFail() {
//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().GetString("log.level").Return("INFO").AnyTimes()
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)
@ -135,6 +139,7 @@ func (suite *RepositoryTestSuite) TestCreateUser_WithUserProvidedId_ShouldBeRepl
//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().GetString("log.level").Return("INFO").AnyTimes()
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)
@ -160,6 +165,7 @@ func (suite *RepositoryTestSuite) TestGetUserByUsername() {
//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().GetString("log.level").Return("INFO").AnyTimes()
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)
@ -183,6 +189,7 @@ func (suite *RepositoryTestSuite) TestGetUserByUsername_WithInvalidUsername() {
//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().GetString("log.level").Return("INFO").AnyTimes()
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)
@ -205,6 +212,7 @@ func (suite *RepositoryTestSuite) TestGetCurrentUser_WithContextBackgroundAuthUs
//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().GetString("log.level").Return("INFO").AnyTimes()
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)
@ -229,6 +237,7 @@ func (suite *RepositoryTestSuite) TestGetCurrentUser_WithGinContextBackgroundAut
//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().GetString("log.level").Return("INFO").AnyTimes()
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)
@ -257,6 +266,7 @@ func (suite *RepositoryTestSuite) TestGetCurrentUser_WithContextBackgroundAuthUs
//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().GetString("log.level").Return("INFO").AnyTimes()
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)
@ -273,6 +283,7 @@ func (suite *RepositoryTestSuite) TestCreateGlossaryEntry() {
//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().GetString("log.level").Return("INFO").AnyTimes()
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)
@ -308,6 +319,7 @@ func (suite *RepositoryTestSuite) TestUpsertRawResource() {
//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().GetString("log.level").Return("INFO").AnyTimes()
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)
@ -364,6 +376,7 @@ func (suite *RepositoryTestSuite) TestUpsertRawResource_WithRelatedResourceAndDu
//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().GetString("log.level").Return("INFO").AnyTimes()
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)
@ -411,6 +424,7 @@ func (suite *RepositoryTestSuite) TestListResources() {
//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().GetString("log.level").Return("INFO").AnyTimes()
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)
@ -513,6 +527,7 @@ func (suite *RepositoryTestSuite) TestGetResourceByResourceTypeAndId() {
//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().GetString("log.level").Return("INFO").AnyTimes()
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)
@ -566,6 +581,7 @@ func (suite *RepositoryTestSuite) TestGetResourceBySourceId() {
//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().GetString("log.level").Return("INFO").AnyTimes()
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)
@ -619,6 +635,7 @@ func (suite *RepositoryTestSuite) TestGetPatientForSources() {
//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().GetString("log.level").Return("INFO").AnyTimes()
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)
@ -675,6 +692,7 @@ func (suite *RepositoryTestSuite) TestAddResourceAssociation() {
//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().GetString("log.level").Return("INFO").AnyTimes()
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)
@ -711,6 +729,7 @@ func (suite *RepositoryTestSuite) TestAddResourceAssociation_WithMismatchingSour
//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().GetString("log.level").Return("INFO").AnyTimes()
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)
@ -760,6 +779,7 @@ func (suite *RepositoryTestSuite) TestRemoveResourceAssociation() {
//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().GetString("log.level").Return("INFO").AnyTimes()
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)
@ -801,6 +821,7 @@ func (suite *RepositoryTestSuite) TestGetSourceSummary() {
//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().GetString("log.level").Return("INFO").AnyTimes()
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)
@ -881,6 +902,7 @@ func (suite *RepositoryTestSuite) TestGetSummary() {
//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().GetString("log.level").Return("INFO").AnyTimes()
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)
@ -984,6 +1006,7 @@ func (suite *RepositoryTestSuite) TestAddResourceComposition() {
//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().GetString("log.level").Return("INFO").AnyTimes()
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)
@ -1069,6 +1092,7 @@ func (suite *RepositoryTestSuite) TestAddResourceComposition_WithExistingComposi
//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().GetString("log.level").Return("INFO").AnyTimes()
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)
@ -1229,6 +1253,7 @@ func (suite *RepositoryTestSuite) TestCreateBackgroundJob_Sync() {
//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().GetString("log.level").Return("INFO").AnyTimes()
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)
@ -1263,6 +1288,7 @@ func (suite *RepositoryTestSuite) TestListBackgroundJobs() {
//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().GetString("log.level").Return("INFO").AnyTimes()
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)
@ -1339,6 +1365,7 @@ func (suite *RepositoryTestSuite) TestUpdateBackgroundJob() {
//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().GetString("log.level").Return("INFO").AnyTimes()
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
require.NoError(suite.T(), err)

View File

@ -0,0 +1,75 @@
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"
databaseModel "github.com/fastenhealth/fasten-onprem/backend/pkg/models/database"
"github.com/sirupsen/logrus"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
func newPostgresRepository(appConfig config.Interface, globalLogger logrus.FieldLogger, eventBus event_bus.Interface) (DatabaseRepository, error) {
//backgroundContext := context.Background()
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Gorm/PostgreSQL setup
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
globalLogger.Infof("Trying to connect to postgres db: %s\n", appConfig.GetString("database.location"))
dsn := appConfig.GetString("database.location")
database, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
//TODO: figure out how to log database queries again.
//logger: logger
DisableForeignKeyConstraintWhenMigrating: true,
})
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 postgres db: %s\n", dsn)
fastenRepo := GormRepository{
AppConfig: appConfig,
Logger: globalLogger,
GormClient: database,
EventBus: eventBus,
}
//TODO: automigrate for now, this should be replaced with a migration tool once the DB has stabilized.
err = fastenRepo.Migrate()
if err != nil {
return nil, err
}
//automigrate Fhir Resource Tables
err = databaseModel.Migrate(fastenRepo.GormClient)
if err != nil {
return nil, err
}
// create/update admin user
//TODO: determine if this admin user is ncessary
//SECURITY: validate this user is necessary
adminUser := models.User{}
err = database.FirstOrCreate(&adminUser, models.User{Username: "admin"}).Error
if err != nil {
return nil, fmt.Errorf("Failed to create admin user! - %v", err)
}
//fail any Locked jobs. This is necessary because the job may have been locked by a process that was killed.
err = fastenRepo.CancelAllLockedBackgroundJobsAndFail()
if err != nil {
return nil, err
}
return &fastenRepo, nil
}

File diff suppressed because it is too large Load Diff

View File

@ -17,3 +17,10 @@ type ConfigValidationError string
func (str ConfigValidationError) Error() string {
return fmt.Sprintf("ConfigValidationError: %q", string(str))
}
// Raised when the database type is unsupported
type DatabaseTypeNotSupportedError string
func (str DatabaseTypeNotSupportedError) Error() string {
return fmt.Sprintf("DatabaseTypeNotSupportedError: %q", string(str))
}

View File

@ -6,13 +6,17 @@ package main
import (
"encoding/json"
"fmt"
"github.com/dave/jennifer/jen"
"github.com/iancoleman/strcase"
"golang.org/x/exp/slices"
"io/ioutil"
"log"
"os"
"sort"
"strings"
"github.com/dave/jennifer/jen"
"github.com/fastenhealth/fasten-onprem/backend/pkg/config"
"github.com/fastenhealth/fasten-onprem/backend/pkg/errors"
"github.com/iancoleman/strcase"
"golang.org/x/exp/slices"
)
type SearchParameter struct {
@ -47,6 +51,22 @@ PLEASE DO NOT EDIT BY HAND
`, "\n"), "\n")
func main() {
// Read config file for database type
appconfig, err := config.Create()
if err != nil {
fmt.Printf("FATAL: %+v\n", err)
os.Exit(1)
}
// Find and read the config file
err = appconfig.ReadConfig("../../../../config.yaml")
if _, ok := err.(errors.ConfigFileMissingError); ok { // Handle errors reading the config file
//ignore "could not find config file"
} else if err != nil {
os.Exit(1)
}
databaseType := appconfig.GetString("database.type")
// Read the search-parameters.json file
searchParamsData, err := ioutil.ReadFile("search-parameters.json")
if err != nil {
@ -188,10 +208,18 @@ func main() {
golangFieldStatement = g.Id(fieldName).Id(golangFieldType)
}
}
golangFieldStatement.Tag(map[string]string{
"json": fmt.Sprintf("%s,omitempty", strcase.ToLowerCamel(fieldName)),
"gorm": fmt.Sprintf("column:%s;%s", strcase.ToLowerCamel(fieldName), mapGormType(fieldInfo.FieldType)),
})
if databaseType == "sqlite" {
golangFieldStatement.Tag(map[string]string{
"json": fmt.Sprintf("%s,omitempty", strcase.ToLowerCamel(fieldName)),
"gorm": fmt.Sprintf("column:%s;%s", strcase.ToLowerCamel(fieldName), mapGormTypeSqlite(fieldInfo.FieldType)),
})
} else {
golangFieldStatement.Tag(map[string]string{
"json": fmt.Sprintf("%s,omitempty", strcase.ToLowerCamel(fieldName)),
"gorm": fmt.Sprintf("column:%s;%s", strcase.ToLowerCamel(fieldName), mapGormTypePostgres(fieldInfo.FieldType)),
})
}
}
})
@ -663,7 +691,7 @@ func mapFieldType(fieldType string) string {
}
// https://www.sqlite.org/datatype3.html
func mapGormType(fieldType string) string {
func mapGormTypeSqlite(fieldType string) string {
// gorm:"type:text;serializer:json"
switch fieldType {
@ -687,3 +715,26 @@ func mapGormType(fieldType string) string {
return "type:text"
}
}
func mapGormTypePostgres(fieldType string) string {
switch fieldType {
case "number":
return "type:real"
case "token":
return "type:text;serializer:json"
case "reference":
return "type:text;serializer:json"
case "date":
return "type:timestamptz"
case "string":
return "type:text;serializer:json"
case "uri":
return "type:text"
case "special":
return "type:text;serializer:json"
case "quantity":
return "type:text;serializer:json"
default:
return "type:text"
}
}

View File

@ -51,6 +51,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().GetString("log.level").Return("INFO").AnyTimes()
suite.AppConfig = appConfig

View File

@ -20,9 +20,10 @@ web:
frontend:
path: /opt/fasten/web
database:
location: '/opt/fasten/db/fasten.db'
type: 'sqlite' # postgres also supported, but still experimental changes
location: '/opt/fasten/db/fasten.db' # if postgres use a DSN, eg. `host=localhost user=gorm password=gorm dbname=gorm port=9920 sslmode=required TimeZone=Asia/Shanghai`
log:
file: '' #absolute or relative paths allowed, eg. web.log
file: '' # absolute or relative paths allowed, eg. web.log
level: INFO
jwt:
issuer:

9
go.mod
View File

@ -27,7 +27,13 @@ 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/gorm v1.24.1
gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55
)
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
)
require (
@ -104,6 +110,7 @@ require (
gopkg.in/yaml.v2 v2.4.0 // indirect
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

8
go.sum
View File

@ -470,6 +470,8 @@ github.com/jackc/pgproto3/v2 v2.2.0 h1:r7JypeP2D3onoQTCxWdTpCtJ4D+qpKr0TxvoyMhZ5
github.com/jackc/pgproto3/v2 v2.2.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
@ -482,6 +484,8 @@ github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQ
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
github.com/jackc/pgx/v4 v4.15.0 h1:B7dTkXsdILD3MF987WGGCcg+tvLW6bZJdEcqVFeU//w=
github.com/jackc/pgx/v4 v4.15.0/go.mod h1:D/zyOyXiaM1TmVWnOM18p0xdDtdakRBa0RsVGI3U3bw=
github.com/jackc/pgx/v5 v5.4.3 h1:cxFyXhxlvAifxnkKKdlxv8XqUf59tDlYjnV5YYfsJJY=
github.com/jackc/pgx/v5 v5.4.3/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
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=
@ -1286,6 +1290,8 @@ gorm.io/driver/mysql v1.3.2 h1:QJryWiqQ91EvZ0jZL48NOpdlPdMjdip1hQ8bTgo4H7I=
gorm.io/driver/mysql v1.3.2/go.mod h1:ChK6AHbHgDCFZyJp0F+BmVGb06PSIoh9uVYKAlRbb2U=
gorm.io/driver/postgres v1.3.4 h1:evZ7plF+Bp+Lr1mO5NdPvd6M/N98XtwHixGB+y7fdEQ=
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/sqlserver v1.3.1 h1:F5t6ScMzOgy1zukRTIZgLZwKahgt3q1woAILVolKpOI=
@ -1295,6 +1301,8 @@ 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.24.1 h1:CgvzRniUdG67hBAzsxDGOAuq4Te1osVMYsa1eQbd4fs=
gorm.io/gorm v1.24.1/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55 h1:sC1Xj4TYrLqg1n3AN10w871An7wJM0gzgcm8jkIkECQ=
gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=