adding version, and migrate cli.
adding better migration support. migrate between legacy source type to brands,portals and endpoints.
This commit is contained in:
parent
a2165a3431
commit
dec1914b91
|
@ -29,6 +29,9 @@ npm install -g yarn
|
||||||
brew install go
|
brew install go
|
||||||
|
|
||||||
brew install docker
|
brew install docker
|
||||||
|
|
||||||
|
# Go specific tools
|
||||||
|
go install github.com/gzuidhof/tygo@latest
|
||||||
```
|
```
|
||||||
|
|
||||||
# Running Tests
|
# Running Tests
|
||||||
|
|
14
Makefile
14
Makefile
|
@ -23,6 +23,10 @@ serve-frontend-prod: dep-frontend
|
||||||
serve-backend:
|
serve-backend:
|
||||||
go run backend/cmd/fasten/fasten.go start --config ./config.dev.yaml --debug
|
go run backend/cmd/fasten/fasten.go start --config ./config.dev.yaml --debug
|
||||||
|
|
||||||
|
.PHONY: migrate
|
||||||
|
migrate:
|
||||||
|
go run backend/cmd/fasten/fasten.go migrate --config ./config.dev.yaml --debug
|
||||||
|
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
# Backend
|
# Backend
|
||||||
|
@ -36,6 +40,7 @@ clean-backend:
|
||||||
dep-backend:
|
dep-backend:
|
||||||
go mod vendor
|
go mod vendor
|
||||||
|
|
||||||
|
|
||||||
.PHONY: test-backend
|
.PHONY: test-backend
|
||||||
test-backend: dep-backend
|
test-backend: dep-backend
|
||||||
go vet ./...
|
go vet ./...
|
||||||
|
@ -45,6 +50,11 @@ test-backend: dep-backend
|
||||||
test-backend-coverage: dep-backend
|
test-backend-coverage: dep-backend
|
||||||
go test -coverprofile=backend-coverage.txt -covermode=atomic -v ./...
|
go test -coverprofile=backend-coverage.txt -covermode=atomic -v ./...
|
||||||
|
|
||||||
|
.PHONY: generate-backend
|
||||||
|
generate-backend:
|
||||||
|
go generate ./...
|
||||||
|
tygo generate
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
# Frontend
|
# Frontend
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
@ -68,6 +78,10 @@ build-frontend-desktop-sandbox: dep-frontend
|
||||||
build-frontend-desktop-prod: dep-frontend
|
build-frontend-desktop-prod: dep-frontend
|
||||||
cd frontend && yarn build -- -c desktop_prod
|
cd frontend && yarn build -- -c desktop_prod
|
||||||
|
|
||||||
|
.PHONY: build-frontend-offline-sandbox
|
||||||
|
build-frontend-offline-sandbox: dep-frontend
|
||||||
|
cd frontend && yarn build -- -c offline_sandbox
|
||||||
|
|
||||||
|
|
||||||
.PHONY: test-frontend
|
.PHONY: test-frontend
|
||||||
# reduce logging, disable angular-cli analytics for ci environment
|
# reduce logging, disable angular-cli analytics for ci environment
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/analogj/go-util/utils"
|
"github.com/analogj/go-util/utils"
|
||||||
"github.com/fastenhealth/fasten-onprem/backend/pkg/config"
|
"github.com/fastenhealth/fasten-onprem/backend/pkg/config"
|
||||||
|
"github.com/fastenhealth/fasten-onprem/backend/pkg/database"
|
||||||
"github.com/fastenhealth/fasten-onprem/backend/pkg/errors"
|
"github.com/fastenhealth/fasten-onprem/backend/pkg/errors"
|
||||||
"github.com/fastenhealth/fasten-onprem/backend/pkg/event_bus"
|
"github.com/fastenhealth/fasten-onprem/backend/pkg/event_bus"
|
||||||
"github.com/fastenhealth/fasten-onprem/backend/pkg/version"
|
"github.com/fastenhealth/fasten-onprem/backend/pkg/version"
|
||||||
|
@ -136,6 +137,55 @@ func main() {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "version",
|
||||||
|
Usage: "Print the version",
|
||||||
|
Action: func(c *cli.Context) error {
|
||||||
|
fmt.Println(version.VERSION)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "migrate",
|
||||||
|
Usage: "Run database migrations, without starting application",
|
||||||
|
Action: func(c *cli.Context) error {
|
||||||
|
|
||||||
|
if c.IsSet("config") {
|
||||||
|
err = appconfig.ReadConfig(c.String("config")) // Find and read the config file
|
||||||
|
if err != nil { // Handle errors reading the config file
|
||||||
|
//ignore "could not find config file"
|
||||||
|
fmt.Printf("Could not find config file at specified path: %s", c.String("config"))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Bool("debug") {
|
||||||
|
appconfig.Set("log.level", "DEBUG")
|
||||||
|
}
|
||||||
|
|
||||||
|
appLogger, logFile, err := CreateLogger(appconfig)
|
||||||
|
if logFile != nil {
|
||||||
|
defer logFile.Close()
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = database.NewRepository(appconfig, appLogger, event_bus.NewNoopEventBusServer())
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "config",
|
||||||
|
Usage: "Specify the path to the config file",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "debug",
|
||||||
|
Usage: "Enable debug logging",
|
||||||
|
EnvVars: []string{"DEBUG"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,8 +58,8 @@ func (gr *GormRepository) CreateUser(ctx context.Context, user *models.User) err
|
||||||
|
|
||||||
//create Fasten source credential for this user.
|
//create Fasten source credential for this user.
|
||||||
fastenUserCred := models.SourceCredential{
|
fastenUserCred := models.SourceCredential{
|
||||||
UserID: user.ID,
|
UserID: user.ID,
|
||||||
SourceType: sourcePkg.SourceTypeFasten,
|
PlatformType: sourcePkg.PlatformTypeFasten,
|
||||||
}
|
}
|
||||||
fastenUserCredResp := gr.GormClient.Create(&fastenUserCred)
|
fastenUserCredResp := gr.GormClient.Create(&fastenUserCred)
|
||||||
if fastenUserCredResp.Error != nil {
|
if fastenUserCredResp.Error != nil {
|
||||||
|
@ -723,7 +723,7 @@ func (gr *GormRepository) AddResourceComposition(ctx context.Context, compositio
|
||||||
}
|
}
|
||||||
|
|
||||||
//generate placeholder source
|
//generate placeholder source
|
||||||
placeholderSource := models.SourceCredential{UserID: currentUser.ID, SourceType: "manual", ModelBase: models.ModelBase{ID: uuid.Nil}}
|
placeholderSource := models.SourceCredential{UserID: currentUser.ID, PlatformType: "manual", ModelBase: models.ModelBase{ID: uuid.Nil}}
|
||||||
|
|
||||||
existingCompositionResources := []*models.ResourceBase{}
|
existingCompositionResources := []*models.ResourceBase{}
|
||||||
rawResourceLookupTable := map[string]*models.ResourceBase{}
|
rawResourceLookupTable := map[string]*models.ResourceBase{}
|
||||||
|
@ -882,7 +882,7 @@ func (gr *GormRepository) CreateSource(ctx context.Context, sourceCreds *models.
|
||||||
return gr.GormClient.WithContext(ctx).
|
return gr.GormClient.WithContext(ctx).
|
||||||
Where(models.SourceCredential{
|
Where(models.SourceCredential{
|
||||||
UserID: sourceCreds.UserID,
|
UserID: sourceCreds.UserID,
|
||||||
SourceType: sourceCreds.SourceType,
|
EndpointID: sourceCreds.EndpointID,
|
||||||
Patient: sourceCreds.Patient}).
|
Patient: sourceCreds.Patient}).
|
||||||
Assign(*sourceCreds).FirstOrCreate(sourceCreds).Error
|
Assign(*sourceCreds).FirstOrCreate(sourceCreds).Error
|
||||||
}
|
}
|
||||||
|
@ -899,15 +899,14 @@ func (gr *GormRepository) UpdateSource(ctx context.Context, sourceCreds *models.
|
||||||
Where(models.SourceCredential{
|
Where(models.SourceCredential{
|
||||||
ModelBase: models.ModelBase{ID: sourceCreds.ID},
|
ModelBase: models.ModelBase{ID: sourceCreds.ID},
|
||||||
UserID: sourceCreds.UserID,
|
UserID: sourceCreds.UserID,
|
||||||
SourceType: sourceCreds.SourceType,
|
EndpointID: sourceCreds.EndpointID,
|
||||||
}).Updates(models.SourceCredential{
|
}).Updates(models.SourceCredential{
|
||||||
AccessToken: sourceCreds.AccessToken,
|
AccessToken: sourceCreds.AccessToken,
|
||||||
RefreshToken: sourceCreds.RefreshToken,
|
RefreshToken: sourceCreds.RefreshToken,
|
||||||
ExpiresAt: sourceCreds.ExpiresAt,
|
ExpiresAt: sourceCreds.ExpiresAt,
|
||||||
DynamicClientId: sourceCreds.DynamicClientId,
|
DynamicClientId: sourceCreds.DynamicClientId,
|
||||||
DynamicClientRegistrationMode: sourceCreds.DynamicClientRegistrationMode,
|
DynamicClientJWKS: sourceCreds.DynamicClientJWKS,
|
||||||
DynamicClientJWKS: sourceCreds.DynamicClientJWKS,
|
LatestBackgroundJobID: sourceCreds.LatestBackgroundJobID,
|
||||||
LatestBackgroundJobID: sourceCreds.LatestBackgroundJobID,
|
|
||||||
}).Error
|
}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -80,7 +80,8 @@ func (suite *RepositoryGraphTestSuite) TestGetFlattenedResourceGraph() {
|
||||||
ModelBase: models.ModelBase{
|
ModelBase: models.ModelBase{
|
||||||
ID: uuid.New(),
|
ID: uuid.New(),
|
||||||
},
|
},
|
||||||
UserID: userModel.ID,
|
UserID: userModel.ID,
|
||||||
|
PlatformType: sourcePkg.PlatformTypeManual,
|
||||||
}
|
}
|
||||||
err = dbRepo.CreateSource(authContext, &testSourceCredential)
|
err = dbRepo.CreateSource(authContext, &testSourceCredential)
|
||||||
require.NoError(suite.T(), err)
|
require.NoError(suite.T(), err)
|
||||||
|
@ -92,7 +93,7 @@ func (suite *RepositoryGraphTestSuite) TestGetFlattenedResourceGraph() {
|
||||||
"type": "test",
|
"type": "test",
|
||||||
})
|
})
|
||||||
|
|
||||||
manualClient, err := sourceFactory.GetSourceClient(sourcePkg.FastenLighthouseEnvSandbox, sourcePkg.SourceTypeManual, authContext, testLogger, &testSourceCredential)
|
manualClient, err := sourceFactory.GetSourceClient(sourcePkg.FastenLighthouseEnvSandbox, authContext, testLogger, &testSourceCredential)
|
||||||
|
|
||||||
summary, err := manualClient.SyncAllBundle(dbRepo, bundleFile, sourcePkg.FhirVersion401)
|
summary, err := manualClient.SyncAllBundle(dbRepo, bundleFile, sourcePkg.FhirVersion401)
|
||||||
require.NoError(suite.T(), err)
|
require.NoError(suite.T(), err)
|
||||||
|
@ -146,7 +147,8 @@ func (suite *RepositoryGraphTestSuite) TestGetFlattenedResourceGraph_NDJson() {
|
||||||
ModelBase: models.ModelBase{
|
ModelBase: models.ModelBase{
|
||||||
ID: uuid.New(),
|
ID: uuid.New(),
|
||||||
},
|
},
|
||||||
UserID: userModel.ID,
|
UserID: userModel.ID,
|
||||||
|
PlatformType: sourcePkg.PlatformTypeManual,
|
||||||
}
|
}
|
||||||
err = dbRepo.CreateSource(authContext, &testSourceCredential)
|
err = dbRepo.CreateSource(authContext, &testSourceCredential)
|
||||||
require.NoError(suite.T(), err)
|
require.NoError(suite.T(), err)
|
||||||
|
@ -158,7 +160,7 @@ func (suite *RepositoryGraphTestSuite) TestGetFlattenedResourceGraph_NDJson() {
|
||||||
"type": "test",
|
"type": "test",
|
||||||
})
|
})
|
||||||
|
|
||||||
manualClient, err := sourceFactory.GetSourceClient(sourcePkg.FastenLighthouseEnvSandbox, sourcePkg.SourceTypeManual, authContext, testLogger, &testSourceCredential)
|
manualClient, err := sourceFactory.GetSourceClient(sourcePkg.FastenLighthouseEnvSandbox, authContext, testLogger, &testSourceCredential)
|
||||||
|
|
||||||
summary, err := manualClient.SyncAllBundle(dbRepo, bundleFile, sourcePkg.FhirVersion401)
|
summary, err := manualClient.SyncAllBundle(dbRepo, bundleFile, sourcePkg.FhirVersion401)
|
||||||
require.NoError(suite.T(), err)
|
require.NoError(suite.T(), err)
|
||||||
|
|
|
@ -3,10 +3,16 @@ package database
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
_20231017112246 "github.com/fastenhealth/fasten-onprem/backend/pkg/database/migrations/20231017112246"
|
||||||
|
_20231201122541 "github.com/fastenhealth/fasten-onprem/backend/pkg/database/migrations/20231201122541"
|
||||||
|
_0240114092806 "github.com/fastenhealth/fasten-onprem/backend/pkg/database/migrations/20240114092806"
|
||||||
|
_20240114103850 "github.com/fastenhealth/fasten-onprem/backend/pkg/database/migrations/20240114103850"
|
||||||
"github.com/fastenhealth/fasten-onprem/backend/pkg/models"
|
"github.com/fastenhealth/fasten-onprem/backend/pkg/models"
|
||||||
databaseModel "github.com/fastenhealth/fasten-onprem/backend/pkg/models/database"
|
databaseModel "github.com/fastenhealth/fasten-onprem/backend/pkg/models/database"
|
||||||
|
sourceCatalog "github.com/fastenhealth/fasten-sources/catalog"
|
||||||
sourcePkg "github.com/fastenhealth/fasten-sources/pkg"
|
sourcePkg "github.com/fastenhealth/fasten-sources/pkg"
|
||||||
"github.com/go-gormigrate/gormigrate/v2"
|
"github.com/go-gormigrate/gormigrate/v2"
|
||||||
|
"github.com/google/uuid"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -20,15 +26,15 @@ func (gr *GormRepository) Migrate() error {
|
||||||
//use "echo $(date '+%Y%m%d%H%M%S')" to generate new ID's
|
//use "echo $(date '+%Y%m%d%H%M%S')" to generate new ID's
|
||||||
m := gormigrate.New(gr.GormClient, gormMigrateOptions, []*gormigrate.Migration{
|
m := gormigrate.New(gr.GormClient, gormMigrateOptions, []*gormigrate.Migration{
|
||||||
{
|
{
|
||||||
ID: "20231017112246", // base database models //TODO: figure out how to version these correctly (SourceCredential is complicated)
|
ID: "20231017112246",
|
||||||
Migrate: func(tx *gorm.DB) error {
|
Migrate: func(tx *gorm.DB) error {
|
||||||
|
|
||||||
return tx.AutoMigrate(
|
return tx.AutoMigrate(
|
||||||
&models.BackgroundJob{},
|
&_20231017112246.BackgroundJob{},
|
||||||
&models.Glossary{},
|
&_20231017112246.Glossary{},
|
||||||
&models.SourceCredential{},
|
&_20231017112246.SourceCredential{},
|
||||||
&models.UserSettingEntry{},
|
&_20231017112246.UserSettingEntry{},
|
||||||
&models.User{},
|
&_20231017112246.User{},
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -44,7 +50,7 @@ func (gr *GormRepository) Migrate() error {
|
||||||
ID: "20231201122541", // Adding Fasten Source Credential for each user
|
ID: "20231201122541", // Adding Fasten Source Credential for each user
|
||||||
Migrate: func(tx *gorm.DB) error {
|
Migrate: func(tx *gorm.DB) error {
|
||||||
|
|
||||||
users := []models.User{}
|
users := []_20231201122541.User{}
|
||||||
results := tx.Find(&users)
|
results := tx.Find(&users)
|
||||||
if results.Error != nil {
|
if results.Error != nil {
|
||||||
return results.Error
|
return results.Error
|
||||||
|
@ -52,9 +58,9 @@ func (gr *GormRepository) Migrate() error {
|
||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
tx.Logger.Info(context.Background(), fmt.Sprintf("Creating Fasten Source Credential for user: %s", user.ID))
|
tx.Logger.Info(context.Background(), fmt.Sprintf("Creating Fasten Source Credential for user: %s", user.ID))
|
||||||
|
|
||||||
fastenUserCred := models.SourceCredential{
|
fastenUserCred := _20231201122541.SourceCredential{
|
||||||
UserID: user.ID,
|
UserID: user.ID,
|
||||||
SourceType: sourcePkg.SourceTypeFasten,
|
SourceType: string(sourcePkg.PlatformTypeFasten),
|
||||||
}
|
}
|
||||||
fastenUserCredCreateResp := tx.Create(&fastenUserCred)
|
fastenUserCredCreateResp := tx.Create(&fastenUserCred)
|
||||||
if fastenUserCredCreateResp.Error != nil {
|
if fastenUserCredCreateResp.Error != nil {
|
||||||
|
@ -65,6 +71,89 @@ func (gr *GormRepository) Migrate() error {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
ID: "20240114092806", // Adding additional fields to Source Credential
|
||||||
|
Migrate: func(tx *gorm.DB) error {
|
||||||
|
|
||||||
|
err := tx.AutoMigrate(
|
||||||
|
&_0240114092806.SourceCredential{},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
//attempt to populate the endpoint id, portal id and brand id for each existing source credential
|
||||||
|
sourceCredentials := []_0240114092806.SourceCredential{}
|
||||||
|
results := tx.Find(&sourceCredentials)
|
||||||
|
if results.Error != nil {
|
||||||
|
return results.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
for ndx, _ := range sourceCredentials {
|
||||||
|
sourceCredential := &sourceCredentials[ndx]
|
||||||
|
|
||||||
|
if sourceCredential.SourceType == string(sourcePkg.PlatformTypeFasten) || sourceCredential.SourceType == string(sourcePkg.PlatformTypeManual) {
|
||||||
|
tx.Logger.Info(context.Background(), fmt.Sprintf("Updating Legacy SourceType (%s) to PlatformType: %s", sourceCredential.SourceType, sourceCredential.ID))
|
||||||
|
|
||||||
|
sourceCredential.PlatformType = string(sourceCredential.SourceType)
|
||||||
|
|
||||||
|
fastenUpdateSourceCredential := tx.Save(sourceCredential)
|
||||||
|
if fastenUpdateSourceCredential.Error != nil {
|
||||||
|
tx.Logger.Error(context.Background(), fmt.Sprintf("An error occurred update Fasten Source Credential: %s", sourceCredential.ID))
|
||||||
|
return fastenUpdateSourceCredential.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
tx.Logger.Info(context.Background(), fmt.Sprintf("Mapping Legacy SourceType (%s) to Brand, Portal and Endpoint IDs: %s", sourceCredential.SourceType, sourceCredential.ID))
|
||||||
|
|
||||||
|
matchingBrand, matchingPortal, matchingEndpoint, err := sourceCatalog.GetPatientAccessInfoForLegacySourceType(sourceCredential.SourceType, sourceCredential.ApiEndpointBaseUrl)
|
||||||
|
if err != nil {
|
||||||
|
tx.Logger.Error(context.Background(), err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
portalId := uuid.MustParse(matchingPortal.Id)
|
||||||
|
sourceCredential.PortalID = &portalId
|
||||||
|
brandId := uuid.MustParse(matchingBrand.Id)
|
||||||
|
sourceCredential.Display = matchingPortal.Name
|
||||||
|
sourceCredential.BrandID = &brandId
|
||||||
|
sourceCredential.EndpointID = uuid.MustParse(matchingEndpoint.Id)
|
||||||
|
sourceCredential.PlatformType = string(matchingEndpoint.GetPlatformType())
|
||||||
|
|
||||||
|
fastenUpdateSourceCredential := tx.Save(sourceCredential)
|
||||||
|
if fastenUpdateSourceCredential.Error != nil {
|
||||||
|
tx.Logger.Error(context.Background(), fmt.Sprintf("An error occurred update Fasten Source Credential: %s", sourceCredential.ID))
|
||||||
|
return fastenUpdateSourceCredential.Error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "20240114103850", // cleanup unnecessary fields, now that we're using Brands, Portals and Endpoints.
|
||||||
|
Migrate: func(tx *gorm.DB) error {
|
||||||
|
|
||||||
|
return tx.AutoMigrate(
|
||||||
|
&_20240114103850.SourceCredential{},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// run when database is empty
|
||||||
|
m.InitSchema(func(tx *gorm.DB) error {
|
||||||
|
err := tx.AutoMigrate(
|
||||||
|
&models.BackgroundJob{},
|
||||||
|
&models.Glossary{},
|
||||||
|
&models.SourceCredential{},
|
||||||
|
&models.UserSettingEntry{},
|
||||||
|
&models.User{},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err := m.Migrate(); err != nil {
|
if err := m.Migrate(); err != nil {
|
||||||
|
|
|
@ -1293,7 +1293,7 @@ func (suite *RepositoryTestSuite) TestCreateBackgroundJob_Sync() {
|
||||||
authContext := context.WithValue(context.Background(), pkg.ContextKeyTypeAuthUsername, "test_username")
|
authContext := context.WithValue(context.Background(), pkg.ContextKeyTypeAuthUsername, "test_username")
|
||||||
|
|
||||||
//test
|
//test
|
||||||
sourceCredential := models.SourceCredential{ModelBase: models.ModelBase{ID: uuid.New()}, SourceType: sourcePkg.SourceType("bluebutton")}
|
sourceCredential := models.SourceCredential{ModelBase: models.ModelBase{ID: uuid.New()}, PlatformType: sourcePkg.PlatformType("bluebutton")}
|
||||||
backgroundJob := models.NewSyncBackgroundJob(sourceCredential)
|
backgroundJob := models.NewSyncBackgroundJob(sourceCredential)
|
||||||
err = dbRepo.CreateBackgroundJob(
|
err = dbRepo.CreateBackgroundJob(
|
||||||
context.WithValue(authContext, pkg.ContextKeyTypeAuthUsername, "test_username"),
|
context.WithValue(authContext, pkg.ContextKeyTypeAuthUsername, "test_username"),
|
||||||
|
@ -1408,7 +1408,7 @@ func (suite *RepositoryTestSuite) TestUpdateBackgroundJob() {
|
||||||
require.NoError(suite.T(), err)
|
require.NoError(suite.T(), err)
|
||||||
authContext := context.WithValue(context.Background(), pkg.ContextKeyTypeAuthUsername, "test_username")
|
authContext := context.WithValue(context.Background(), pkg.ContextKeyTypeAuthUsername, "test_username")
|
||||||
|
|
||||||
sourceCredential := models.SourceCredential{ModelBase: models.ModelBase{ID: uuid.New()}, SourceType: sourcePkg.SourceType("bluebutton")}
|
sourceCredential := models.SourceCredential{ModelBase: models.ModelBase{ID: uuid.New()}, PlatformType: sourcePkg.PlatformType("bluebutton")}
|
||||||
backgroundJob := models.NewSyncBackgroundJob(sourceCredential)
|
backgroundJob := models.NewSyncBackgroundJob(sourceCredential)
|
||||||
err = dbRepo.CreateBackgroundJob(
|
err = dbRepo.CreateBackgroundJob(
|
||||||
context.WithValue(authContext, pkg.ContextKeyTypeAuthUsername, "test_username"),
|
context.WithValue(authContext, pkg.ContextKeyTypeAuthUsername, "test_username"),
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
package _20231017112246
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fastenhealth/fasten-onprem/backend/pkg"
|
||||||
|
"github.com/fastenhealth/fasten-onprem/backend/pkg/models"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"gorm.io/datatypes"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BackgroundJob struct {
|
||||||
|
models.ModelBase
|
||||||
|
User models.User `json:"user,omitempty"` //SECURITY: user and user.id will be set by the repository service
|
||||||
|
UserID uuid.UUID `json:"user_id"`
|
||||||
|
|
||||||
|
JobType pkg.BackgroundJobType `json:"job_type"`
|
||||||
|
Data datatypes.JSON `gorm:"column:data;type:text;serializer:json" json:"data,omitempty"`
|
||||||
|
JobStatus pkg.BackgroundJobStatus `json:"job_status"`
|
||||||
|
LockedTime *time.Time `json:"locked_time"`
|
||||||
|
DoneTime *time.Time `json:"done_time"`
|
||||||
|
Retries int `json:"retries"`
|
||||||
|
Schedule *pkg.BackgroundJobSchedule `json:"schedule,omitempty"`
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package _20231017112246
|
||||||
|
|
||||||
|
import "github.com/fastenhealth/fasten-onprem/backend/pkg/models"
|
||||||
|
|
||||||
|
// Glossary contains patient friendly terms for medical concepts
|
||||||
|
// Can be retrieved by Code and CodeSystem
|
||||||
|
// Structured similar to ValueSet https://hl7.org/fhir/valueset.html
|
||||||
|
type Glossary struct {
|
||||||
|
models.ModelBase
|
||||||
|
Code string `json:"code" gorm:"uniqueIndex:idx_glossary_term"`
|
||||||
|
CodeSystem string `json:"code_system" gorm:"uniqueIndex:idx_glossary_term"`
|
||||||
|
Publisher string `json:"publisher"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Url string `json:"url"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
package _20231017112246
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fastenhealth/fasten-onprem/backend/pkg/models"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SourceCredential Data/Medical Provider Credentials
|
||||||
|
// similar to LighthouseSourceDefinition from fasten-source
|
||||||
|
type SourceCredential struct {
|
||||||
|
models.ModelBase
|
||||||
|
User *User `json:"user,omitempty"`
|
||||||
|
UserID uuid.UUID `json:"user_id" gorm:"uniqueIndex:idx_user_source_patient"`
|
||||||
|
SourceType string `json:"source_type" gorm:"uniqueIndex:idx_user_source_patient"`
|
||||||
|
Patient string `json:"patient" gorm:"uniqueIndex:idx_user_source_patient"`
|
||||||
|
|
||||||
|
LatestBackgroundJob *BackgroundJob `json:"latest_background_job,omitempty"`
|
||||||
|
LatestBackgroundJobID *uuid.UUID `json:"-"`
|
||||||
|
|
||||||
|
//oauth endpoints
|
||||||
|
AuthorizationEndpoint string `json:"authorization_endpoint"`
|
||||||
|
TokenEndpoint string `json:"token_endpoint"`
|
||||||
|
IntrospectionEndpoint string `json:"introspection_endpoint"`
|
||||||
|
RegistrationEndpoint string `json:"registration_endpoint"` //optional - required when Dynamic Client Registration mode is set
|
||||||
|
|
||||||
|
Scopes []string `json:"scopes_supported" gorm:"type:text;serializer:json"`
|
||||||
|
Issuer string `json:"issuer"`
|
||||||
|
GrantTypesSupported []string `json:"grant_types_supported" gorm:"type:text;serializer:json"`
|
||||||
|
ResponseType []string `json:"response_types_supported" gorm:"type:text;serializer:json"`
|
||||||
|
ResponseModesSupported []string `json:"response_modes_supported" gorm:"type:text;serializer:json"`
|
||||||
|
Audience string `json:"aud"` //optional - required for some providers
|
||||||
|
CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported" gorm:"type:text;serializer:json"`
|
||||||
|
|
||||||
|
//Fasten custom configuration
|
||||||
|
UserInfoEndpoint string `json:"userinfo_endpoint"` //optional - supported by some providers, not others.
|
||||||
|
ApiEndpointBaseUrl string `json:"api_endpoint_base_url"` //api endpoint we'll communicate with after authentication
|
||||||
|
ClientId string `json:"client_id"`
|
||||||
|
RedirectUri string `json:"redirect_uri"` //lighthouse url the provider will redirect to (registered with App)
|
||||||
|
|
||||||
|
Confidential bool `json:"confidential"` //if enabled, requires client_secret to authenticate with provider (PKCE)
|
||||||
|
DynamicClientRegistrationMode string `json:"dynamic_client_registration_mode"` //if enabled, will dynamically register client with provider (https://oauth.net/2/dynamic-client-registration/)
|
||||||
|
CORSRelayRequired bool `json:"cors_relay_required"` //if true, requires CORS proxy/relay, as provider does not return proper response to CORS preflight
|
||||||
|
//SecretKeyPrefix string `json:"-"` //the secret key prefix to use, if empty (default) will use the sourceType value
|
||||||
|
|
||||||
|
// auth/credential data
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
RefreshToken string `json:"refresh_token"`
|
||||||
|
IdToken string `json:"id_token"`
|
||||||
|
ExpiresAt int64 `json:"expires_at"`
|
||||||
|
CodeChallenge string `json:"code_challenge"`
|
||||||
|
CodeVerifier string `json:"code_verifier"`
|
||||||
|
|
||||||
|
//dynamic client auth/credential data
|
||||||
|
DynamicClientJWKS []map[string]string `json:"dynamic_client_jwks" gorm:"type:text;serializer:json"`
|
||||||
|
DynamicClientId string `json:"dynamic_client_id"`
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package _20231017112246
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fastenhealth/fasten-onprem/backend/pkg/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
models.ModelBase
|
||||||
|
FullName string `json:"full_name"`
|
||||||
|
Username string `json:"username" gorm:"unique"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
|
||||||
|
//additional optional metadata that Fasten stores with users
|
||||||
|
Picture string `json:"picture"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
//Roles datatypes.JSON `json:"roles"`
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package _20231017112246
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fastenhealth/fasten-onprem/backend/pkg/models"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserSettingEntry struct {
|
||||||
|
//GORM attributes, see: http://gorm.io/docs/conventions.html
|
||||||
|
models.ModelBase
|
||||||
|
User *User `json:"user,omitempty" gorm:"-"`
|
||||||
|
UserID uuid.UUID `json:"user_id" gorm:"not null;index:,unique,composite:user_setting_key_name"`
|
||||||
|
|
||||||
|
SettingKeyName string `json:"setting_key_name" gorm:"not null;index:,unique,composite:user_setting_key_name"`
|
||||||
|
SettingKeyDescription string `json:"setting_key_description"`
|
||||||
|
SettingDataType string `json:"setting_data_type"`
|
||||||
|
|
||||||
|
SettingValueNumeric int `json:"setting_value_numeric"`
|
||||||
|
SettingValueString string `json:"setting_value_string"`
|
||||||
|
SettingValueBool bool `json:"setting_value_bool"`
|
||||||
|
SettingValueArray []string `json:"setting_value_array" gorm:"column:setting_value_array;type:text;serializer:json"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s UserSettingEntry) TableName() string {
|
||||||
|
return "user_settings"
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package _20231201122541
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fastenhealth/fasten-onprem/backend/pkg"
|
||||||
|
"github.com/fastenhealth/fasten-onprem/backend/pkg/models"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"gorm.io/datatypes"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BackgroundJob struct {
|
||||||
|
models.ModelBase
|
||||||
|
User models.User `json:"user,omitempty"` //SECURITY: user and user.id will be set by the repository service
|
||||||
|
UserID uuid.UUID `json:"user_id"`
|
||||||
|
|
||||||
|
JobType pkg.BackgroundJobType `json:"job_type"`
|
||||||
|
Data datatypes.JSON `gorm:"column:data;type:text;serializer:json" json:"data,omitempty"`
|
||||||
|
JobStatus pkg.BackgroundJobStatus `json:"job_status"`
|
||||||
|
LockedTime *time.Time `json:"locked_time"`
|
||||||
|
DoneTime *time.Time `json:"done_time"`
|
||||||
|
Retries int `json:"retries"`
|
||||||
|
Schedule *pkg.BackgroundJobSchedule `json:"schedule,omitempty"`
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
package _20231201122541
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fastenhealth/fasten-onprem/backend/pkg/models"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SourceCredential Data/Medical Provider Credentials
|
||||||
|
// similar to LighthouseSourceDefinition from fasten-source
|
||||||
|
type SourceCredential struct {
|
||||||
|
models.ModelBase
|
||||||
|
User *User `json:"user,omitempty"`
|
||||||
|
UserID uuid.UUID `json:"user_id" gorm:"uniqueIndex:idx_user_source_patient"`
|
||||||
|
SourceType string `json:"source_type" gorm:"uniqueIndex:idx_user_source_patient"`
|
||||||
|
Patient string `json:"patient" gorm:"uniqueIndex:idx_user_source_patient"`
|
||||||
|
|
||||||
|
LatestBackgroundJob *BackgroundJob `json:"latest_background_job,omitempty"`
|
||||||
|
LatestBackgroundJobID *uuid.UUID `json:"-"`
|
||||||
|
|
||||||
|
//oauth endpoints
|
||||||
|
AuthorizationEndpoint string `json:"authorization_endpoint"`
|
||||||
|
TokenEndpoint string `json:"token_endpoint"`
|
||||||
|
IntrospectionEndpoint string `json:"introspection_endpoint"`
|
||||||
|
RegistrationEndpoint string `json:"registration_endpoint"` //optional - required when Dynamic Client Registration mode is set
|
||||||
|
|
||||||
|
Scopes []string `json:"scopes_supported" gorm:"type:text;serializer:json"`
|
||||||
|
Issuer string `json:"issuer"`
|
||||||
|
GrantTypesSupported []string `json:"grant_types_supported" gorm:"type:text;serializer:json"`
|
||||||
|
ResponseType []string `json:"response_types_supported" gorm:"type:text;serializer:json"`
|
||||||
|
ResponseModesSupported []string `json:"response_modes_supported" gorm:"type:text;serializer:json"`
|
||||||
|
Audience string `json:"aud"` //optional - required for some providers
|
||||||
|
CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported" gorm:"type:text;serializer:json"`
|
||||||
|
|
||||||
|
//Fasten custom configuration
|
||||||
|
UserInfoEndpoint string `json:"userinfo_endpoint"` //optional - supported by some providers, not others.
|
||||||
|
ApiEndpointBaseUrl string `json:"api_endpoint_base_url"` //api endpoint we'll communicate with after authentication
|
||||||
|
ClientId string `json:"client_id"`
|
||||||
|
RedirectUri string `json:"redirect_uri"` //lighthouse url the provider will redirect to (registered with App)
|
||||||
|
|
||||||
|
Confidential bool `json:"confidential"` //if enabled, requires client_secret to authenticate with provider (PKCE)
|
||||||
|
DynamicClientRegistrationMode string `json:"dynamic_client_registration_mode"` //if enabled, will dynamically register client with provider (https://oauth.net/2/dynamic-client-registration/)
|
||||||
|
CORSRelayRequired bool `json:"cors_relay_required"` //if true, requires CORS proxy/relay, as provider does not return proper response to CORS preflight
|
||||||
|
//SecretKeyPrefix string `json:"-"` //the secret key prefix to use, if empty (default) will use the sourceType value
|
||||||
|
|
||||||
|
// auth/credential data
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
RefreshToken string `json:"refresh_token"`
|
||||||
|
IdToken string `json:"id_token"`
|
||||||
|
ExpiresAt int64 `json:"expires_at"`
|
||||||
|
CodeChallenge string `json:"code_challenge"`
|
||||||
|
CodeVerifier string `json:"code_verifier"`
|
||||||
|
|
||||||
|
//dynamic client auth/credential data
|
||||||
|
DynamicClientJWKS []map[string]string `json:"dynamic_client_jwks" gorm:"type:text;serializer:json"`
|
||||||
|
DynamicClientId string `json:"dynamic_client_id"`
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package _20231201122541
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fastenhealth/fasten-onprem/backend/pkg/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
models.ModelBase
|
||||||
|
FullName string `json:"full_name"`
|
||||||
|
Username string `json:"username" gorm:"unique"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
|
||||||
|
//additional optional metadata that Fasten stores with users
|
||||||
|
Picture string `json:"picture"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
//Roles datatypes.JSON `json:"roles"`
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
package _0240114092806
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fastenhealth/fasten-onprem/backend/pkg/models"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SourceCredential struct {
|
||||||
|
models.ModelBase
|
||||||
|
User *models.User `json:"user,omitempty"`
|
||||||
|
UserID uuid.UUID `json:"user_id" gorm:"uniqueIndex:idx_user_source_patient"`
|
||||||
|
SourceType string `json:"source_type" gorm:"uniqueIndex:idx_user_source_patient"`
|
||||||
|
Patient string `json:"patient" gorm:"uniqueIndex:idx_user_source_patient"`
|
||||||
|
|
||||||
|
//New Fields
|
||||||
|
Display string `json:"display"`
|
||||||
|
BrandID *uuid.UUID `json:"brand_id"`
|
||||||
|
PortalID *uuid.UUID `json:"portal_id"`
|
||||||
|
EndpointID uuid.UUID `json:"endpoint_id"`
|
||||||
|
PlatformType string `json:"platform_type"`
|
||||||
|
|
||||||
|
LatestBackgroundJob *models.BackgroundJob `json:"latest_background_job,omitempty"`
|
||||||
|
LatestBackgroundJobID *uuid.UUID `json:"-"`
|
||||||
|
|
||||||
|
//oauth endpoints
|
||||||
|
AuthorizationEndpoint string `json:"authorization_endpoint"`
|
||||||
|
TokenEndpoint string `json:"token_endpoint"`
|
||||||
|
IntrospectionEndpoint string `json:"introspection_endpoint"`
|
||||||
|
RegistrationEndpoint string `json:"registration_endpoint"` //optional - required when Dynamic Client Registration mode is set
|
||||||
|
|
||||||
|
Scopes []string `json:"scopes_supported" gorm:"type:text;serializer:json"`
|
||||||
|
Issuer string `json:"issuer"`
|
||||||
|
GrantTypesSupported []string `json:"grant_types_supported" gorm:"type:text;serializer:json"`
|
||||||
|
ResponseType []string `json:"response_types_supported" gorm:"type:text;serializer:json"`
|
||||||
|
ResponseModesSupported []string `json:"response_modes_supported" gorm:"type:text;serializer:json"`
|
||||||
|
Audience string `json:"aud"` //optional - required for some providers
|
||||||
|
CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported" gorm:"type:text;serializer:json"`
|
||||||
|
|
||||||
|
//Fasten custom configuration
|
||||||
|
UserInfoEndpoint string `json:"userinfo_endpoint"` //optional - supported by some providers, not others.
|
||||||
|
ApiEndpointBaseUrl string `json:"api_endpoint_base_url"` //api endpoint we'll communicate with after authentication
|
||||||
|
ClientId string `json:"client_id"`
|
||||||
|
RedirectUri string `json:"redirect_uri"` //lighthouse url the provider will redirect to (registered with App)
|
||||||
|
|
||||||
|
Confidential bool `json:"confidential"` //if enabled, requires client_secret to authenticate with provider (PKCE)
|
||||||
|
CORSRelayRequired bool `json:"cors_relay_required"` //if true, requires CORS proxy/relay, as provider does not return proper response to CORS preflight
|
||||||
|
//SecretKeyPrefix string `json:"-"` //the secret key prefix to use, if empty (default) will use the sourceType value
|
||||||
|
|
||||||
|
// auth/credential data
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
RefreshToken string `json:"refresh_token"`
|
||||||
|
IdToken string `json:"id_token"`
|
||||||
|
ExpiresAt int64 `json:"expires_at"`
|
||||||
|
CodeChallenge string `json:"code_challenge"`
|
||||||
|
CodeVerifier string `json:"code_verifier"`
|
||||||
|
|
||||||
|
//dynamic client auth/credential data
|
||||||
|
DynamicClientJWKS []map[string]string `json:"dynamic_client_jwks" gorm:"type:text;serializer:json"`
|
||||||
|
DynamicClientId string `json:"dynamic_client_id"`
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
package _20240114103850
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fastenhealth/fasten-onprem/backend/pkg/models"
|
||||||
|
sourcesPkg "github.com/fastenhealth/fasten-sources/pkg"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SourceCredential Data/Medical Provider Credentials
|
||||||
|
// similar to LighthouseSourceDefinition from fasten-source
|
||||||
|
type SourceCredential struct {
|
||||||
|
models.ModelBase
|
||||||
|
User *models.User `json:"user,omitempty"`
|
||||||
|
UserID uuid.UUID `json:"user_id" gorm:"uniqueIndex:idx_user_source_patient"`
|
||||||
|
Patient string `json:"patient" gorm:"uniqueIndex:idx_user_source_patient"`
|
||||||
|
EndpointID uuid.UUID `json:"endpoint_id" gorm:"uniqueIndex:idx_user_source_patient"`
|
||||||
|
|
||||||
|
//New Fields
|
||||||
|
Display string `json:"display"`
|
||||||
|
BrandID uuid.UUID `json:"brand_id"`
|
||||||
|
PortalID uuid.UUID `json:"portal_id"`
|
||||||
|
PlatformType sourcesPkg.PlatformType `json:"platform_type"`
|
||||||
|
|
||||||
|
LatestBackgroundJob *models.BackgroundJob `json:"latest_background_job,omitempty"`
|
||||||
|
LatestBackgroundJobID *uuid.UUID `json:"-"`
|
||||||
|
|
||||||
|
// auth/credential data
|
||||||
|
ClientId string `json:"client_id"`
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
RefreshToken string `json:"refresh_token"`
|
||||||
|
IdToken string `json:"id_token"`
|
||||||
|
ExpiresAt int64 `json:"expires_at"`
|
||||||
|
CodeChallenge string `json:"code_challenge"`
|
||||||
|
CodeVerifier string `json:"code_verifier"`
|
||||||
|
|
||||||
|
//dynamic client auth/credential data
|
||||||
|
DynamicClientJWKS []map[string]string `json:"dynamic_client_jwks" gorm:"type:text;serializer:json"`
|
||||||
|
DynamicClientId string `json:"dynamic_client_id"`
|
||||||
|
}
|
|
@ -11,7 +11,7 @@ func NewSyncBackgroundJob(source SourceCredential) *BackgroundJob {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
data := BackgroundJobSyncData{
|
data := BackgroundJobSyncData{
|
||||||
SourceID: source.ID,
|
SourceID: source.ID,
|
||||||
SourceType: string(source.SourceType),
|
BrandID: source.BrandID.String(),
|
||||||
CheckpointData: nil,
|
CheckpointData: nil,
|
||||||
ErrorData: nil,
|
ErrorData: nil,
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ func NewSyncBackgroundJob(source SourceCredential) *BackgroundJob {
|
||||||
|
|
||||||
type BackgroundJobSyncData struct {
|
type BackgroundJobSyncData struct {
|
||||||
SourceID uuid.UUID `json:"source_id"`
|
SourceID uuid.UUID `json:"source_id"`
|
||||||
SourceType string `json:"source_type"`
|
BrandID string `json:"brand_id"`
|
||||||
CheckpointData map[string]interface{} `json:"checkpoint_data,omitempty"`
|
CheckpointData map[string]interface{} `json:"checkpoint_data,omitempty"`
|
||||||
ErrorData map[string]interface{} `json:"error_data,omitempty"`
|
ErrorData map[string]interface{} `json:"error_data,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/fastenhealth/fasten-onprem/backend/pkg/jwk"
|
"github.com/fastenhealth/fasten-onprem/backend/pkg/jwk"
|
||||||
|
sourcesDefinitions "github.com/fastenhealth/fasten-sources/definitions"
|
||||||
sourcesPkg "github.com/fastenhealth/fasten-sources/pkg"
|
sourcesPkg "github.com/fastenhealth/fasten-sources/pkg"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/lestrrat-go/jwx/v2/jwa"
|
"github.com/lestrrat-go/jwx/v2/jwa"
|
||||||
|
@ -20,40 +21,22 @@ import (
|
||||||
// similar to LighthouseSourceDefinition from fasten-source
|
// similar to LighthouseSourceDefinition from fasten-source
|
||||||
type SourceCredential struct {
|
type SourceCredential struct {
|
||||||
ModelBase
|
ModelBase
|
||||||
User *User `json:"user,omitempty"`
|
User *User `json:"user,omitempty"`
|
||||||
UserID uuid.UUID `json:"user_id" gorm:"uniqueIndex:idx_user_source_patient"`
|
UserID uuid.UUID `json:"user_id" gorm:"uniqueIndex:idx_user_source_patient"`
|
||||||
SourceType sourcesPkg.SourceType `json:"source_type" gorm:"uniqueIndex:idx_user_source_patient"`
|
Patient string `json:"patient" gorm:"uniqueIndex:idx_user_source_patient"`
|
||||||
Patient string `json:"patient" gorm:"uniqueIndex:idx_user_source_patient"`
|
EndpointID uuid.UUID `json:"endpoint_id" gorm:"uniqueIndex:idx_user_source_patient"`
|
||||||
|
|
||||||
|
//New Fields
|
||||||
|
Display string `json:"display"`
|
||||||
|
BrandID uuid.UUID `json:"brand_id"`
|
||||||
|
PortalID uuid.UUID `json:"portal_id"`
|
||||||
|
PlatformType sourcesPkg.PlatformType `json:"platform_type"`
|
||||||
|
|
||||||
LatestBackgroundJob *BackgroundJob `json:"latest_background_job,omitempty"`
|
LatestBackgroundJob *BackgroundJob `json:"latest_background_job,omitempty"`
|
||||||
LatestBackgroundJobID *uuid.UUID `json:"-"`
|
LatestBackgroundJobID *uuid.UUID `json:"-"`
|
||||||
|
|
||||||
//oauth endpoints
|
|
||||||
AuthorizationEndpoint string `json:"authorization_endpoint"`
|
|
||||||
TokenEndpoint string `json:"token_endpoint"`
|
|
||||||
IntrospectionEndpoint string `json:"introspection_endpoint"`
|
|
||||||
RegistrationEndpoint string `json:"registration_endpoint"` //optional - required when Dynamic Client Registration mode is set
|
|
||||||
|
|
||||||
Scopes []string `json:"scopes_supported" gorm:"type:text;serializer:json"`
|
|
||||||
Issuer string `json:"issuer"`
|
|
||||||
GrantTypesSupported []string `json:"grant_types_supported" gorm:"type:text;serializer:json"`
|
|
||||||
ResponseType []string `json:"response_types_supported" gorm:"type:text;serializer:json"`
|
|
||||||
ResponseModesSupported []string `json:"response_modes_supported" gorm:"type:text;serializer:json"`
|
|
||||||
Audience string `json:"aud"` //optional - required for some providers
|
|
||||||
CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported" gorm:"type:text;serializer:json"`
|
|
||||||
|
|
||||||
//Fasten custom configuration
|
|
||||||
UserInfoEndpoint string `json:"userinfo_endpoint"` //optional - supported by some providers, not others.
|
|
||||||
ApiEndpointBaseUrl string `json:"api_endpoint_base_url"` //api endpoint we'll communicate with after authentication
|
|
||||||
ClientId string `json:"client_id"`
|
|
||||||
RedirectUri string `json:"redirect_uri"` //lighthouse url the provider will redirect to (registered with App)
|
|
||||||
|
|
||||||
Confidential bool `json:"confidential"` //if enabled, requires client_secret to authenticate with provider (PKCE)
|
|
||||||
DynamicClientRegistrationMode string `json:"dynamic_client_registration_mode"` //if enabled, will dynamically register client with provider (https://oauth.net/2/dynamic-client-registration/)
|
|
||||||
CORSRelayRequired bool `json:"cors_relay_required"` //if true, requires CORS proxy/relay, as provider does not return proper response to CORS preflight
|
|
||||||
//SecretKeyPrefix string `json:"-"` //the secret key prefix to use, if empty (default) will use the sourceType value
|
|
||||||
|
|
||||||
// auth/credential data
|
// auth/credential data
|
||||||
|
ClientId string `json:"client_id"`
|
||||||
AccessToken string `json:"access_token"`
|
AccessToken string `json:"access_token"`
|
||||||
RefreshToken string `json:"refresh_token"`
|
RefreshToken string `json:"refresh_token"`
|
||||||
IdToken string `json:"id_token"`
|
IdToken string `json:"id_token"`
|
||||||
|
@ -66,13 +49,26 @@ type SourceCredential struct {
|
||||||
DynamicClientId string `json:"dynamic_client_id"`
|
DynamicClientId string `json:"dynamic_client_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SourceCredential) GetSourceType() sourcesPkg.SourceType {
|
|
||||||
return s.SourceType
|
|
||||||
}
|
|
||||||
func (s *SourceCredential) GetSourceId() string {
|
func (s *SourceCredential) GetSourceId() string {
|
||||||
return s.ID.String()
|
return s.ID.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SourceCredential) GetEndpointId() string {
|
||||||
|
return s.EndpointID.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SourceCredential) GetPortalId() string {
|
||||||
|
return s.PortalID.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SourceCredential) GetBrandId() string {
|
||||||
|
return s.BrandID.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SourceCredential) GetPlatformType() sourcesPkg.PlatformType {
|
||||||
|
return s.PlatformType
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SourceCredential) GetClientId() string {
|
func (s *SourceCredential) GetClientId() string {
|
||||||
return s.ClientId
|
return s.ClientId
|
||||||
}
|
}
|
||||||
|
@ -81,18 +77,6 @@ func (s *SourceCredential) GetPatientId() string {
|
||||||
return s.Patient
|
return s.Patient
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SourceCredential) GetOauthAuthorizationEndpoint() string {
|
|
||||||
return s.AuthorizationEndpoint
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SourceCredential) GetOauthTokenEndpoint() string {
|
|
||||||
return s.TokenEndpoint
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SourceCredential) GetApiEndpointBaseUrl() string {
|
|
||||||
return s.ApiEndpointBaseUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SourceCredential) GetRefreshToken() string {
|
func (s *SourceCredential) GetRefreshToken() string {
|
||||||
return s.RefreshToken
|
return s.RefreshToken
|
||||||
}
|
}
|
||||||
|
@ -135,7 +119,14 @@ codeVerifier: codeVerifier
|
||||||
|
|
||||||
// IsDynamicClient this method is used to check if this source uses dynamic client registration (used to customize token refresh logic)
|
// IsDynamicClient this method is used to check if this source uses dynamic client registration (used to customize token refresh logic)
|
||||||
func (s *SourceCredential) IsDynamicClient() bool {
|
func (s *SourceCredential) IsDynamicClient() bool {
|
||||||
return len(s.DynamicClientRegistrationMode) > 0
|
|
||||||
|
endpoint, err := sourcesDefinitions.GetSourceDefinition(sourcesDefinitions.GetSourceConfigOptions{
|
||||||
|
EndpointId: s.EndpointID.String(),
|
||||||
|
})
|
||||||
|
if err != nil || endpoint == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return len(endpoint.DynamicClientRegistrationMode) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method will generate a new keypair, register a new dynamic client with the provider
|
// This method will generate a new keypair, register a new dynamic client with the provider
|
||||||
|
@ -144,6 +135,17 @@ func (s *SourceCredential) IsDynamicClient() bool {
|
||||||
// - DynamicClientId
|
// - DynamicClientId
|
||||||
func (s *SourceCredential) RegisterDynamicClient() error {
|
func (s *SourceCredential) RegisterDynamicClient() error {
|
||||||
|
|
||||||
|
endpoint, err := sourcesDefinitions.GetSourceDefinition(sourcesDefinitions.GetSourceConfigOptions{
|
||||||
|
EndpointId: s.EndpointID.String(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("an error occurred while retrieving source definition: %w", err)
|
||||||
|
} else if endpoint == nil {
|
||||||
|
return fmt.Errorf("endpoint definition not found")
|
||||||
|
} else if endpoint.RegistrationEndpoint == "" {
|
||||||
|
return fmt.Errorf("registration endpoint not found")
|
||||||
|
}
|
||||||
|
|
||||||
//this source requires dynamic client registration
|
//this source requires dynamic client registration
|
||||||
// see https://fhir.epic.com/Documentation?docId=Oauth2§ion=Standalone-Oauth2-OfflineAccess-0
|
// see https://fhir.epic.com/Documentation?docId=Oauth2§ion=Standalone-Oauth2-OfflineAccess-0
|
||||||
|
|
||||||
|
@ -183,7 +185,7 @@ func (s *SourceCredential) RegisterDynamicClient() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
//http.Post("https://fhir.epic.com/interconnect-fhir-oauth/oauth2/token", "application/x-www-form-urlencoded", bytes.NewBuffer([]byte(fmt.Sprintf("grant_type=client_credentials&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&client_assertion=%s&scope=system/Patient.read", sourceSpecificClientKeyPair))))
|
//http.Post("https://fhir.epic.com/interconnect-fhir-oauth/oauth2/token", "application/x-www-form-urlencoded", bytes.NewBuffer([]byte(fmt.Sprintf("grant_type=client_credentials&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&client_assertion=%s&scope=system/Patient.read", sourceSpecificClientKeyPair))))
|
||||||
req, err := http.NewRequest(http.MethodPost, s.RegistrationEndpoint, bytes.NewBuffer(payloadBytes))
|
req, err := http.NewRequest(http.MethodPost, endpoint.RegistrationEndpoint, bytes.NewBuffer(payloadBytes))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("an error occurred while generating dynamic client registration request: %w", err)
|
return fmt.Errorf("an error occurred while generating dynamic client registration request: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -223,7 +225,14 @@ func (s *SourceCredential) RegisterDynamicClient() error {
|
||||||
// - AccessToken
|
// - AccessToken
|
||||||
// - ExpiresAt
|
// - ExpiresAt
|
||||||
func (s *SourceCredential) RefreshDynamicClientAccessToken() error {
|
func (s *SourceCredential) RefreshDynamicClientAccessToken() error {
|
||||||
if len(s.DynamicClientRegistrationMode) == 0 {
|
endpoint, err := sourcesDefinitions.GetSourceDefinition(sourcesDefinitions.GetSourceConfigOptions{
|
||||||
|
EndpointId: s.EndpointID.String(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("an error occurred while retrieving source definition: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(endpoint.DynamicClientRegistrationMode) == 0 {
|
||||||
return fmt.Errorf("dynamic client registration mode not set")
|
return fmt.Errorf("dynamic client registration mode not set")
|
||||||
}
|
}
|
||||||
if len(s.DynamicClientJWKS) == 0 {
|
if len(s.DynamicClientJWKS) == 0 {
|
||||||
|
@ -243,7 +252,7 @@ func (s *SourceCredential) RefreshDynamicClientAccessToken() error {
|
||||||
t := jwt.New()
|
t := jwt.New()
|
||||||
t.Set("kid", jwkeypair.KeyID())
|
t.Set("kid", jwkeypair.KeyID())
|
||||||
t.Set(jwt.SubjectKey, s.DynamicClientId)
|
t.Set(jwt.SubjectKey, s.DynamicClientId)
|
||||||
t.Set(jwt.AudienceKey, s.TokenEndpoint)
|
t.Set(jwt.AudienceKey, endpoint.TokenEndpoint)
|
||||||
t.Set(jwt.JwtIDKey, uuid.New().String())
|
t.Set(jwt.JwtIDKey, uuid.New().String())
|
||||||
t.Set(jwt.ExpirationKey, time.Now().Add(time.Minute*2).Unix()) // must be less than 5 minutes from now. Time when this JWT expires
|
t.Set(jwt.ExpirationKey, time.Now().Add(time.Minute*2).Unix()) // must be less than 5 minutes from now. Time when this JWT expires
|
||||||
t.Set(jwt.IssuedAtKey, time.Now().Unix())
|
t.Set(jwt.IssuedAtKey, time.Now().Unix())
|
||||||
|
@ -265,7 +274,7 @@ func (s *SourceCredential) RefreshDynamicClientAccessToken() error {
|
||||||
"client_id": {s.DynamicClientId},
|
"client_id": {s.DynamicClientId},
|
||||||
}
|
}
|
||||||
|
|
||||||
tokenResp, err := http.PostForm(s.TokenEndpoint, postForm)
|
tokenResp, err := http.PostForm(endpoint.TokenEndpoint, postForm)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("an error occurred while sending dynamic client token request, %s", err)
|
return fmt.Errorf("an error occurred while sending dynamic client token request, %s", err)
|
||||||
|
|
|
@ -36,7 +36,7 @@ func BackgroundJobSyncResources(
|
||||||
_sourceCred *models.SourceCredential,
|
_sourceCred *models.SourceCredential,
|
||||||
) (sourceModels.SourceClient, sourceModels.UpsertSummary, error) {
|
) (sourceModels.SourceClient, sourceModels.UpsertSummary, error) {
|
||||||
// after creating the client, we should do a bulk import
|
// after creating the client, we should do a bulk import
|
||||||
sourceClient, err := factory.GetSourceClient(sourcePkg.GetFastenLighthouseEnv(), _sourceCred.SourceType, _backgroundJobContext, _logger, _sourceCred)
|
sourceClient, err := factory.GetSourceClient(sourcePkg.GetFastenLighthouseEnv(), _backgroundJobContext, _logger, _sourceCred)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resultErr := fmt.Errorf("an error occurred while initializing hub client using source credential: %w", err)
|
resultErr := fmt.Errorf("an error occurred while initializing hub client using source credential: %w", err)
|
||||||
_logger.Errorln(resultErr)
|
_logger.Errorln(resultErr)
|
||||||
|
|
|
@ -32,7 +32,7 @@ func CreateRelatedResources(c *gin.Context) {
|
||||||
sourceCredentials, err := databaseRepo.GetSources(c)
|
sourceCredentials, err := databaseRepo.GetSources(c)
|
||||||
var fastenSourceCredential *models.SourceCredential
|
var fastenSourceCredential *models.SourceCredential
|
||||||
for _, sourceCredential := range sourceCredentials {
|
for _, sourceCredential := range sourceCredentials {
|
||||||
if sourceCredential.SourceType == sourcePkg.SourceTypeFasten {
|
if sourceCredential.PlatformType == sourcePkg.PlatformTypeFasten {
|
||||||
fastenSourceCredential = &sourceCredential
|
fastenSourceCredential = &sourceCredential
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ func CreateRelatedResources(c *gin.Context) {
|
||||||
) (sourceModels.SourceClient, sourceModels.UpsertSummary, error) {
|
) (sourceModels.SourceClient, sourceModels.UpsertSummary, error) {
|
||||||
|
|
||||||
//step 3: create a "fasten" client, which we can use to parse resources to add to the database
|
//step 3: create a "fasten" client, which we can use to parse resources to add to the database
|
||||||
fastenSourceClient, err := factory.GetSourceClient(sourcePkg.GetFastenLighthouseEnv(), sourcePkg.SourceTypeFasten, _backgroundJobContext, _logger, _sourceCred)
|
fastenSourceClient, err := factory.GetSourceClient(sourcePkg.GetFastenLighthouseEnv(), _backgroundJobContext, _logger, _sourceCred)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resultErr := fmt.Errorf("could not create Fasten source client")
|
resultErr := fmt.Errorf("could not create Fasten source client")
|
||||||
_logger.Errorln(resultErr)
|
_logger.Errorln(resultErr)
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/fastenhealth/fasten-onprem/backend/pkg/models"
|
"github.com/fastenhealth/fasten-onprem/backend/pkg/models"
|
||||||
"github.com/fastenhealth/fasten-sources/clients/factory"
|
"github.com/fastenhealth/fasten-sources/clients/factory"
|
||||||
sourceModels "github.com/fastenhealth/fasten-sources/clients/models"
|
sourceModels "github.com/fastenhealth/fasten-sources/clients/models"
|
||||||
|
sourceDefinitions "github.com/fastenhealth/fasten-sources/definitions"
|
||||||
sourcePkg "github.com/fastenhealth/fasten-sources/pkg"
|
sourcePkg "github.com/fastenhealth/fasten-sources/pkg"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
@ -31,11 +32,16 @@ func CreateReconnectSource(c *gin.Context) {
|
||||||
|
|
||||||
logger.Infof("Parsed Create SourceCredential Credentials Payload: %v", sourceCred)
|
logger.Infof("Parsed Create SourceCredential Credentials Payload: %v", sourceCred)
|
||||||
|
|
||||||
if sourceCred.DynamicClientRegistrationMode == "user-authenticated" {
|
//get the endpoint definition
|
||||||
|
endpointDefinition, err := sourceDefinitions.GetSourceDefinition(sourceDefinitions.GetSourceConfigOptions{
|
||||||
|
EndpointId: sourceCred.EndpointID.String(),
|
||||||
|
})
|
||||||
|
|
||||||
|
if endpointDefinition.DynamicClientRegistrationMode == "user-authenticated" {
|
||||||
logger.Warnf("This client requires a dynamic client registration, starting registration process")
|
logger.Warnf("This client requires a dynamic client registration, starting registration process")
|
||||||
|
|
||||||
if len(sourceCred.RegistrationEndpoint) == 0 {
|
if len(endpointDefinition.RegistrationEndpoint) == 0 {
|
||||||
err := fmt.Errorf("this client requires dynamic registration, but does not provide a registration endpoint: %s", sourceCred.DynamicClientRegistrationMode)
|
err := fmt.Errorf("this client requires dynamic registration, but does not provide a registration endpoint: %s", endpointDefinition.DynamicClientRegistrationMode)
|
||||||
logger.Errorln(err)
|
logger.Errorln(err)
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": err.Error()})
|
c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": err.Error()})
|
||||||
return
|
return
|
||||||
|
@ -143,9 +149,9 @@ func CreateManualSource(c *gin.Context) {
|
||||||
|
|
||||||
// create a "manual" client, which we can use to parse the
|
// create a "manual" client, which we can use to parse the
|
||||||
manualSourceCredential := models.SourceCredential{
|
manualSourceCredential := models.SourceCredential{
|
||||||
SourceType: sourcePkg.SourceTypeManual,
|
PlatformType: sourcePkg.PlatformTypeManual,
|
||||||
}
|
}
|
||||||
tempSourceClient, err := factory.GetSourceClient(sourcePkg.GetFastenLighthouseEnv(), sourcePkg.SourceTypeManual, c, logger, &manualSourceCredential)
|
tempSourceClient, err := factory.GetSourceClient(sourcePkg.GetFastenLighthouseEnv(), c, logger, &manualSourceCredential)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorln("An error occurred while initializing hub client using manual source without credentials", err)
|
logger.Errorln("An error occurred while initializing hub client using manual source without credentials", err)
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||||
|
@ -179,7 +185,7 @@ func CreateManualSource(c *gin.Context) {
|
||||||
_databaseRepo database.DatabaseRepository,
|
_databaseRepo database.DatabaseRepository,
|
||||||
_sourceCred *models.SourceCredential,
|
_sourceCred *models.SourceCredential,
|
||||||
) (sourceModels.SourceClient, sourceModels.UpsertSummary, error) {
|
) (sourceModels.SourceClient, sourceModels.UpsertSummary, error) {
|
||||||
manualSourceClient, err := factory.GetSourceClient(sourcePkg.GetFastenLighthouseEnv(), sourcePkg.SourceTypeManual, _backgroundJobContext, _logger, _sourceCred)
|
manualSourceClient, err := factory.GetSourceClient(sourcePkg.GetFastenLighthouseEnv(), _backgroundJobContext, _logger, _sourceCred)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resultErr := fmt.Errorf("an error occurred while initializing hub client using manual source with credential: %w", err)
|
resultErr := fmt.Errorf("an error occurred while initializing hub client using manual source with credential: %w", err)
|
||||||
logger.Errorln(resultErr)
|
logger.Errorln(resultErr)
|
||||||
|
|
|
@ -77,27 +77,24 @@ func (suite *SourceHandlerTestSuite) AfterTest(suiteName, testName string) {
|
||||||
os.Remove(suite.TestDatabase.Name())
|
os.Remove(suite.TestDatabase.Name())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func CreateManualSourceHttpRequestFromFile(fileName string) (*http.Request, error) {
|
func CreateManualSourceHttpRequestFromFile(fileName string) (*http.Request, error) {
|
||||||
|
|
||||||
file, err := os.Open(fileName)
|
file, err := os.Open(fileName)
|
||||||
if err!= nil {
|
if err != nil {
|
||||||
log.Fatal("Could not open file ", err.Error())
|
log.Fatal("Could not open file ", err.Error())
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
|
|
||||||
body := &bytes.Buffer{}
|
body := &bytes.Buffer{}
|
||||||
writer := multipart.NewWriter(body)
|
writer := multipart.NewWriter(body)
|
||||||
part, _ := writer.CreateFormFile("file", filepath.Base(file.Name()))
|
part, _ := writer.CreateFormFile("file", filepath.Base(file.Name()))
|
||||||
io.Copy(part, file)
|
io.Copy(part, file)
|
||||||
writer.Close()
|
writer.Close()
|
||||||
|
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", "/source/manual", body)
|
req, err := http.NewRequest("POST", "/source/manual", body)
|
||||||
if err!= nil {
|
if err != nil {
|
||||||
log.Fatal("Could not make http request ", err.Error())
|
log.Fatal("Could not make http request ", err.Error())
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
req.Header.Add("Content-Type", writer.FormDataContentType())
|
req.Header.Add("Content-Type", writer.FormDataContentType())
|
||||||
|
@ -123,7 +120,7 @@ func (suite *SourceHandlerTestSuite) TestCreateManualSourceHandler() {
|
||||||
|
|
||||||
//test
|
//test
|
||||||
req, err := CreateManualSourceHttpRequestFromFile("testdata/Tania553_Harris789_545c2380-b77f-4919-ab5d-0f615f877250.json")
|
req, err := CreateManualSourceHttpRequestFromFile("testdata/Tania553_Harris789_545c2380-b77f-4919-ab5d-0f615f877250.json")
|
||||||
require.NoError(suite.T(),err)
|
require.NoError(suite.T(), err)
|
||||||
ctx.Request = req
|
ctx.Request = req
|
||||||
|
|
||||||
CreateManualSource(ctx)
|
CreateManualSource(ctx)
|
||||||
|
@ -144,7 +141,7 @@ func (suite *SourceHandlerTestSuite) TestCreateManualSourceHandler() {
|
||||||
require.NoError(suite.T(), err)
|
require.NoError(suite.T(), err)
|
||||||
|
|
||||||
require.Equal(suite.T(), true, respWrapper.Success)
|
require.Equal(suite.T(), true, respWrapper.Success)
|
||||||
require.Equal(suite.T(), "manual", string(respWrapper.Source.SourceType))
|
require.Equal(suite.T(), "manual", string(respWrapper.Source.PlatformType))
|
||||||
require.Equal(suite.T(), 196, respWrapper.Data.TotalResources)
|
require.Equal(suite.T(), 196, respWrapper.Data.TotalResources)
|
||||||
summary, err := suite.AppRepository.GetSourceSummary(ctx, respWrapper.Source.ID.String())
|
summary, err := suite.AppRepository.GetSourceSummary(ctx, respWrapper.Source.ID.String())
|
||||||
require.NoError(suite.T(), err)
|
require.NoError(suite.T(), err)
|
||||||
|
|
|
@ -46,7 +46,7 @@ func UnsafeRequestSource(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := factory.GetSourceClient(sourcePkg.GetFastenLighthouseEnv(), foundSource.SourceType, c, logger, foundSource)
|
client, err := factory.GetSourceClient(sourcePkg.GetFastenLighthouseEnv(), c, logger, foundSource)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("Could not initialize source client %v", err)
|
logger.Errorf("Could not initialize source client %v", err)
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": err.Error()})
|
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": err.Error()})
|
||||||
|
@ -140,7 +140,7 @@ func UnsafeSyncResourceNames(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := factory.GetSourceClient(sourcePkg.GetFastenLighthouseEnv(), foundSource.SourceType, c, logger, foundSource)
|
client, err := factory.GetSourceClient(sourcePkg.GetFastenLighthouseEnv(), c, logger, foundSource)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("Could not initialize source client %v", err)
|
logger.Errorf("Could not initialize source client %v", err)
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": err.Error()})
|
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": err.Error()})
|
||||||
|
|
4
go.mod
4
go.mod
|
@ -15,7 +15,7 @@ require (
|
||||||
github.com/dave/jennifer v1.6.1
|
github.com/dave/jennifer v1.6.1
|
||||||
github.com/dominikbraun/graph v0.15.0
|
github.com/dominikbraun/graph v0.15.0
|
||||||
github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3
|
github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3
|
||||||
github.com/fastenhealth/fasten-sources v0.4.21
|
github.com/fastenhealth/fasten-sources v0.5.0
|
||||||
github.com/fastenhealth/gofhir-models v0.0.6
|
github.com/fastenhealth/gofhir-models v0.0.6
|
||||||
github.com/gin-gonic/gin v1.9.0
|
github.com/gin-gonic/gin v1.9.0
|
||||||
github.com/go-gormigrate/gormigrate/v2 v2.1.1
|
github.com/go-gormigrate/gormigrate/v2 v2.1.1
|
||||||
|
@ -60,7 +60,7 @@ require (
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
github.com/go-playground/locales v0.14.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/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.13.0 // indirect
|
github.com/go-playground/validator/v10 v10.16.0 // indirect
|
||||||
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
|
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
|
||||||
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
||||||
github.com/goccy/go-json v0.10.2 // indirect
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
|
|
8
go.sum
8
go.sum
|
@ -101,8 +101,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
|
||||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/fastenhealth/fasten-sources v0.4.21 h1:zs6un6SpK4zkzUzignHtsaWpdEx9MNbORjUFZ6DoFDg=
|
github.com/fastenhealth/fasten-sources v0.5.0 h1:VQIayggGOdGlosnIrgcPMsDH/xqDjvz4jbRx4DQfZ3w=
|
||||||
github.com/fastenhealth/fasten-sources v0.4.21/go.mod h1:tnvfgYG9utKCQ18+Jp26QBOO1ncwAkYx2l/XOBJqnyw=
|
github.com/fastenhealth/fasten-sources v0.5.0/go.mod h1:hUQATAu5KrxKbACJoVt4iEKIGnRtmiOmHz+5TLfyiCM=
|
||||||
github.com/fastenhealth/gofhir-models v0.0.6 h1:yJYYaV1eJtHiGEfA1rXLsyOm/9hIi6s2cGoZzGfW1tM=
|
github.com/fastenhealth/gofhir-models v0.0.6 h1:yJYYaV1eJtHiGEfA1rXLsyOm/9hIi6s2cGoZzGfW1tM=
|
||||||
github.com/fastenhealth/gofhir-models v0.0.6/go.mod h1:xB8ikGxu3bUq2b1JYV+CZpHqBaLXpOizFR0eFBCunis=
|
github.com/fastenhealth/gofhir-models v0.0.6/go.mod h1:xB8ikGxu3bUq2b1JYV+CZpHqBaLXpOizFR0eFBCunis=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
|
@ -129,8 +129,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
|
||||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
github.com/go-playground/validator/v10 v10.13.0 h1:cFRQdfaSMCOSfGCCLB20MHvuoHb/s5G8L5pu2ppK5AQ=
|
github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE=
|
||||||
github.com/go-playground/validator/v10 v10.13.0/go.mod h1:dwu7+CG8/CtBiJFZDz4e+5Upb6OLw04gtBYw0mcG/z4=
|
github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||||
github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU=
|
github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU=
|
||||||
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
|
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
|
||||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||||
|
|
Loading…
Reference in New Issue