refactor backend to remove all storage logic. Just auth and metadata endpoints now.
Adding docker image for couchdb and addign docker-compose file.
This commit is contained in:
parent
f6681a8e62
commit
f2bb44e8be
|
@ -1,56 +0,0 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/golang-jwt/jwt"
|
||||
"time"
|
||||
)
|
||||
|
||||
//TODO: this should match the ID and username for the user.
|
||||
type JWTClaim struct {
|
||||
Username string `json:"username"`
|
||||
UserId string `json:"user_id"`
|
||||
Email string `json:"email"`
|
||||
jwt.StandardClaims
|
||||
}
|
||||
|
||||
func GenerateJWT(encryptionKey string, username string, userId string) (tokenString string, err error) {
|
||||
expirationTime := time.Now().Add(2 * time.Hour)
|
||||
claims := &JWTClaim{
|
||||
Username: username,
|
||||
Email: username,
|
||||
UserId: userId,
|
||||
StandardClaims: jwt.StandardClaims{
|
||||
ExpiresAt: expirationTime.Unix(),
|
||||
},
|
||||
}
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
tokenString, err = token.SignedString([]byte(encryptionKey))
|
||||
return
|
||||
}
|
||||
|
||||
func ValidateToken(encryptionKey string, signedToken string) (*JWTClaim, error) {
|
||||
token, err := jwt.ParseWithClaims(
|
||||
signedToken,
|
||||
&JWTClaim{},
|
||||
func(token *jwt.Token) (interface{}, error) {
|
||||
if jwt.SigningMethodHS256 != token.Method {
|
||||
return nil, errors.New("Invalid signing algorithm")
|
||||
}
|
||||
return []byte(encryptionKey), nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
claims, ok := token.Claims.(*JWTClaim)
|
||||
if !ok {
|
||||
err = errors.New("couldn't parse claims")
|
||||
return nil, err
|
||||
}
|
||||
if claims.ExpiresAt < time.Now().Local().Unix() {
|
||||
err = errors.New("token expired")
|
||||
return nil, err
|
||||
}
|
||||
return claims, nil
|
||||
}
|
|
@ -27,6 +27,12 @@ func (c *configuration) Init() error {
|
|||
c.SetDefault("web.src.frontend.path", "/opt/fasten/web")
|
||||
c.SetDefault("web.database.location", "/opt/fasten/db/fasten.db") //TODO: should be /opt/fasten/fasten.db
|
||||
|
||||
c.SetDefault("web.couchdb.scheme", "http")
|
||||
c.SetDefault("web.couchdb.host", "localhost")
|
||||
c.SetDefault("web.couchdb.port", "5984")
|
||||
c.SetDefault("web.couchdb.admin_username", "admin")
|
||||
c.SetDefault("web.couchdb.admin_password", "mysecretpassword")
|
||||
|
||||
c.SetDefault("log.level", "INFO")
|
||||
c.SetDefault("log.file", "")
|
||||
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"github.com/go-kivik/couchdb/v3"
|
||||
_ "github.com/go-kivik/couchdb/v3" // The CouchDB driver
|
||||
"github.com/go-kivik/kivik/v3"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func NewRepository(appConfig config.Interface, globalLogger logrus.FieldLogger) (DatabaseRepository, error) {
|
||||
//backgroundContext := context.Background()
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Gorm/SQLite setup
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
globalLogger.Infof("Trying to connect to sqlite db: %s\n", appConfig.GetString("web.database.location"))
|
||||
|
||||
// When a transaction cannot lock the database, because it is already locked by another one,
|
||||
// SQLite by default throws an error: database is locked. This behavior is usually not appropriate when
|
||||
// concurrent access is needed, typically when multiple processes write to the same database.
|
||||
// PRAGMA busy_timeout lets you set a timeout or a handler for these events. When setting a timeout,
|
||||
// SQLite will try the transaction multiple times within this timeout.
|
||||
// fixes #341
|
||||
// https://rsqlite.r-dbi.org/reference/sqlitesetbusyhandler
|
||||
// retrying for 30000 milliseconds, 30seconds - this would be unreasonable for a distributed multi-tenant application,
|
||||
// but should be fine for local usage.
|
||||
|
||||
couchdbUrl := fmt.Sprintf("%s://%s:%s", appConfig.GetString("web.couchdb.scheme"), appConfig.GetString("web.couchdb.host"), appConfig.GetString("web.couchdb.port"))
|
||||
database, err := kivik.New("couch", couchdbUrl)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to connect to database! - %v", err)
|
||||
}
|
||||
|
||||
err = database.Authenticate(context.Background(),
|
||||
couchdb.BasicAuth(
|
||||
appConfig.GetString("web.couchdb.admin_username"),
|
||||
appConfig.GetString("web.couchdb.admin_password")),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to authenticate to database! - %v", err)
|
||||
}
|
||||
globalLogger.Infof("Successfully connected to coubdb: %s\n", couchdbUrl)
|
||||
|
||||
deviceRepo := couchdbRepository{
|
||||
appConfig: appConfig,
|
||||
logger: globalLogger,
|
||||
client: database,
|
||||
}
|
||||
return &deviceRepo, nil
|
||||
}
|
||||
|
||||
type couchdbRepository struct {
|
||||
appConfig config.Interface
|
||||
logger logrus.FieldLogger
|
||||
|
||||
client *kivik.Client
|
||||
}
|
||||
|
||||
type couchDbUser struct {
|
||||
ID string `json:"_id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Roles []string `json:"roles"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
func (cr *couchdbRepository) CreateUser(ctx context.Context, user *models.User) error {
|
||||
|
||||
newUser := &couchDbUser{
|
||||
ID: fmt.Sprintf("%s%s", kivik.UserPrefix, user.Username),
|
||||
Name: user.Username,
|
||||
Type: "user",
|
||||
Roles: []string{},
|
||||
Password: user.Password,
|
||||
}
|
||||
db := cr.client.DB(ctx, "_users")
|
||||
_, err := db.Put(ctx, newUser.ID, newUser)
|
||||
return err
|
||||
}
|
||||
|
||||
func (cr *couchdbRepository) Close() error {
|
||||
return nil
|
||||
}
|
|
@ -10,21 +10,4 @@ type DatabaseRepository interface {
|
|||
Close() error
|
||||
|
||||
CreateUser(context.Context, *models.User) error
|
||||
GetUserByEmail(context.Context, string) (*models.User, error)
|
||||
GetCurrentUser(context.Context) *models.User
|
||||
|
||||
GetSummary(ctx context.Context) (*models.Summary, error)
|
||||
|
||||
UpsertResource(context.Context, *models.ResourceFhir) error
|
||||
GetResourceBySourceType(context.Context, string, string) (*models.ResourceFhir, error)
|
||||
GetResourceBySourceId(context.Context, string, string) (*models.ResourceFhir, error)
|
||||
ListResources(context.Context, models.ListResourceQueryOptions) ([]models.ResourceFhir, error)
|
||||
GetPatientForSources(ctx context.Context) ([]models.ResourceFhir, error)
|
||||
//UpsertProfile(context.Context, *models.Profile) error
|
||||
//UpsertOrganziation(context.Context, *models.Organization) error
|
||||
|
||||
CreateSource(context.Context, *models.Source) error
|
||||
GetSource(context.Context, string) (*models.Source, error)
|
||||
GetSourceSummary(context.Context, string) (*models.SourceSummary, error)
|
||||
GetSources(context.Context) ([]models.Source, error)
|
||||
}
|
||||
|
|
|
@ -1,378 +0,0 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/glebarez/sqlite"
|
||||
"github.com/google/uuid"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gorm.io/gorm"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
func NewRepository(appConfig config.Interface, globalLogger logrus.FieldLogger) (DatabaseRepository, error) {
|
||||
//backgroundContext := context.Background()
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Gorm/SQLite setup
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
globalLogger.Infof("Trying to connect to sqlite db: %s\n", appConfig.GetString("web.database.location"))
|
||||
|
||||
// When a transaction cannot lock the database, because it is already locked by another one,
|
||||
// SQLite by default throws an error: database is locked. This behavior is usually not appropriate when
|
||||
// concurrent access is needed, typically when multiple processes write to the same database.
|
||||
// PRAGMA busy_timeout lets you set a timeout or a handler for these events. When setting a timeout,
|
||||
// SQLite will try the transaction multiple times within this timeout.
|
||||
// fixes #341
|
||||
// https://rsqlite.r-dbi.org/reference/sqlitesetbusyhandler
|
||||
// retrying for 30000 milliseconds, 30seconds - this would be unreasonable for a distributed multi-tenant application,
|
||||
// but should be fine for local usage.
|
||||
pragmaStr := sqlitePragmaString(map[string]string{
|
||||
"busy_timeout": "30000",
|
||||
"foreign_keys": "ON",
|
||||
})
|
||||
database, err := gorm.Open(sqlite.Open(appConfig.GetString("web.database.location")+pragmaStr), &gorm.Config{
|
||||
//TODO: figure out how to log database queries again.
|
||||
//Logger: logger
|
||||
DisableForeignKeyConstraintWhenMigrating: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to connect to database! - %v", err)
|
||||
}
|
||||
globalLogger.Infof("Successfully connected to scrutiny sqlite db: %s\n", appConfig.GetString("web.database.location"))
|
||||
|
||||
//TODO: automigrate for now
|
||||
err = database.AutoMigrate(
|
||||
&models.User{},
|
||||
&models.Source{},
|
||||
&models.ResourceFhir{},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to automigrate! - %v", err)
|
||||
}
|
||||
|
||||
// create/update admin user
|
||||
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)
|
||||
}
|
||||
|
||||
deviceRepo := sqliteRepository{
|
||||
appConfig: appConfig,
|
||||
logger: globalLogger,
|
||||
gormClient: database,
|
||||
}
|
||||
return &deviceRepo, nil
|
||||
}
|
||||
|
||||
type sqliteRepository struct {
|
||||
appConfig config.Interface
|
||||
logger logrus.FieldLogger
|
||||
|
||||
gormClient *gorm.DB
|
||||
}
|
||||
|
||||
func (sr *sqliteRepository) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// User
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func (sr *sqliteRepository) CreateUser(ctx context.Context, user *models.User) error {
|
||||
if err := user.HashPassword(user.Password); err != nil {
|
||||
return err
|
||||
}
|
||||
record := sr.gormClient.Create(user)
|
||||
if record.Error != nil {
|
||||
return record.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (sr *sqliteRepository) GetUserByEmail(ctx context.Context, username string) (*models.User, error) {
|
||||
var foundUser models.User
|
||||
result := sr.gormClient.Where(models.User{Username: username}).First(&foundUser)
|
||||
return &foundUser, result.Error
|
||||
}
|
||||
|
||||
func (sr *sqliteRepository) GetCurrentUser(ctx context.Context) *models.User {
|
||||
ginCtx := ctx.(*gin.Context)
|
||||
var currentUser models.User
|
||||
sr.gormClient.First(¤tUser, models.User{Username: ginCtx.MustGet("AUTH_USERNAME").(string)})
|
||||
|
||||
return ¤tUser
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// User
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func (sr *sqliteRepository) GetSummary(ctx context.Context) (*models.Summary, error) {
|
||||
|
||||
// we want a count of all resources for this user by type
|
||||
var resourceCountResults []map[string]interface{}
|
||||
|
||||
//group by resource type and return counts
|
||||
// SELECT source_resource_type as resource_type, COUNT(*) as count FROM resource_fhirs WHERE source_id = "53c1e930-63af-46c9-b760-8e83cbc1abd9" GROUP BY source_resource_type;
|
||||
result := sr.gormClient.WithContext(ctx).
|
||||
Model(models.ResourceFhir{}).
|
||||
Select("source_id, source_resource_type as resource_type, count(*) as count").
|
||||
Group("source_resource_type").
|
||||
Where(models.OriginBase{
|
||||
UserID: sr.GetCurrentUser(ctx).ID,
|
||||
}).
|
||||
Scan(&resourceCountResults)
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
|
||||
// we want a list of all sources (when they were last updated)
|
||||
sources, err := sr.GetSources(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// we want the main Patient for each source
|
||||
patients, err := sr.GetPatientForSources(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
summary := &models.Summary{
|
||||
Sources: sources,
|
||||
ResourceTypeCounts: resourceCountResults,
|
||||
Patients: patients,
|
||||
}
|
||||
|
||||
return summary, nil
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Resource
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func (sr *sqliteRepository) UpsertResource(ctx context.Context, resourceModel *models.ResourceFhir) error {
|
||||
sr.logger.Infof("insert/update (%T) %v", resourceModel, resourceModel)
|
||||
|
||||
if sr.gormClient.Debug().WithContext(ctx).
|
||||
Where(models.OriginBase{
|
||||
SourceID: resourceModel.GetSourceID(),
|
||||
SourceResourceID: resourceModel.GetSourceResourceID(),
|
||||
SourceResourceType: resourceModel.GetSourceResourceType(), //TODO: and UpdatedAt > old UpdatedAt
|
||||
}).Updates(resourceModel).RowsAffected == 0 {
|
||||
sr.logger.Infof("resource does not exist, creating: %s %s %s", resourceModel.GetSourceID(), resourceModel.GetSourceResourceID(), resourceModel.GetSourceResourceType())
|
||||
return sr.gormClient.Debug().Create(resourceModel).Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sr *sqliteRepository) ListResources(ctx context.Context, queryOptions models.ListResourceQueryOptions) ([]models.ResourceFhir, error) {
|
||||
|
||||
queryParam := models.ResourceFhir{
|
||||
OriginBase: models.OriginBase{
|
||||
UserID: sr.GetCurrentUser(ctx).ID,
|
||||
},
|
||||
}
|
||||
|
||||
if len(queryOptions.SourceResourceType) > 0 {
|
||||
queryParam.OriginBase.SourceResourceType = queryOptions.SourceResourceType
|
||||
}
|
||||
|
||||
if len(queryOptions.SourceID) > 0 {
|
||||
sourceUUID, err := uuid.Parse(queryOptions.SourceID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
queryParam.OriginBase.SourceID = sourceUUID
|
||||
}
|
||||
|
||||
manifestJson, _ := json.MarshalIndent(queryParam, "", " ")
|
||||
sr.logger.Infof("THE QUERY OBJECT===========> %v", string(manifestJson))
|
||||
|
||||
var wrappedResourceModels []models.ResourceFhir
|
||||
results := sr.gormClient.WithContext(ctx).
|
||||
Where(queryParam).
|
||||
Find(&wrappedResourceModels)
|
||||
|
||||
return wrappedResourceModels, results.Error
|
||||
}
|
||||
|
||||
func (sr *sqliteRepository) GetResourceBySourceType(ctx context.Context, sourceResourceType string, sourceResourceId string) (*models.ResourceFhir, error) {
|
||||
queryParam := models.ResourceFhir{
|
||||
OriginBase: models.OriginBase{
|
||||
UserID: sr.GetCurrentUser(ctx).ID,
|
||||
SourceResourceType: sourceResourceType,
|
||||
SourceResourceID: sourceResourceId,
|
||||
},
|
||||
}
|
||||
|
||||
var wrappedResourceModel models.ResourceFhir
|
||||
results := sr.gormClient.WithContext(ctx).
|
||||
Where(queryParam).
|
||||
First(&wrappedResourceModel)
|
||||
|
||||
return &wrappedResourceModel, results.Error
|
||||
}
|
||||
|
||||
func (sr *sqliteRepository) GetResourceBySourceId(ctx context.Context, sourceId string, sourceResourceId string) (*models.ResourceFhir, error) {
|
||||
sourceIdUUID, err := uuid.Parse(sourceId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
queryParam := models.ResourceFhir{
|
||||
OriginBase: models.OriginBase{
|
||||
UserID: sr.GetCurrentUser(ctx).ID,
|
||||
SourceID: sourceIdUUID,
|
||||
SourceResourceID: sourceResourceId,
|
||||
},
|
||||
}
|
||||
|
||||
var wrappedResourceModel models.ResourceFhir
|
||||
results := sr.gormClient.WithContext(ctx).
|
||||
Where(queryParam).
|
||||
First(&wrappedResourceModel)
|
||||
|
||||
return &wrappedResourceModel, results.Error
|
||||
}
|
||||
|
||||
// Get the patient for each source (for the current user)
|
||||
func (sr *sqliteRepository) GetPatientForSources(ctx context.Context) ([]models.ResourceFhir, error) {
|
||||
|
||||
//SELECT * FROM resource_fhirs WHERE user_id = "" and source_resource_type = "Patient" GROUP BY source_id
|
||||
|
||||
//var sourceCred models.Source
|
||||
//results := sr.gormClient.WithContext(ctx).
|
||||
// Where(models.Source{UserID: sr.GetCurrentUser(ctx).ID, ModelBase: models.ModelBase{ID: sourceUUID}}).
|
||||
// First(&sourceCred)
|
||||
|
||||
var wrappedResourceModels []models.ResourceFhir
|
||||
results := sr.gormClient.WithContext(ctx).
|
||||
Model(models.ResourceFhir{}).
|
||||
Group("source_id").
|
||||
Where(models.OriginBase{
|
||||
UserID: sr.GetCurrentUser(ctx).ID,
|
||||
SourceResourceType: "Patient",
|
||||
}).
|
||||
Find(&wrappedResourceModels)
|
||||
|
||||
return wrappedResourceModels, results.Error
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Source
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func (sr *sqliteRepository) CreateSource(ctx context.Context, sourceCreds *models.Source) error {
|
||||
sourceCreds.UserID = sr.GetCurrentUser(ctx).ID
|
||||
|
||||
if sr.gormClient.WithContext(ctx).
|
||||
Where(models.Source{
|
||||
UserID: sourceCreds.UserID,
|
||||
SourceType: sourceCreds.SourceType,
|
||||
PatientId: sourceCreds.PatientId}).Updates(sourceCreds).RowsAffected == 0 {
|
||||
return sr.gormClient.WithContext(ctx).Create(sourceCreds).Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sr *sqliteRepository) GetSource(ctx context.Context, sourceId string) (*models.Source, error) {
|
||||
sourceUUID, err := uuid.Parse(sourceId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var sourceCred models.Source
|
||||
results := sr.gormClient.WithContext(ctx).
|
||||
Where(models.Source{UserID: sr.GetCurrentUser(ctx).ID, ModelBase: models.ModelBase{ID: sourceUUID}}).
|
||||
First(&sourceCred)
|
||||
|
||||
return &sourceCred, results.Error
|
||||
}
|
||||
|
||||
func (sr *sqliteRepository) GetSourceSummary(ctx context.Context, sourceId string) (*models.SourceSummary, error) {
|
||||
sourceUUID, err := uuid.Parse(sourceId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sourceSummary := &models.SourceSummary{}
|
||||
|
||||
source, err := sr.GetSource(ctx, sourceId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sourceSummary.Source = source
|
||||
|
||||
//group by resource type and return counts
|
||||
// SELECT source_resource_type as resource_type, COUNT(*) as count FROM resource_fhirs WHERE source_id = "53c1e930-63af-46c9-b760-8e83cbc1abd9" GROUP BY source_resource_type;
|
||||
|
||||
var resourceTypeCounts []map[string]interface{}
|
||||
|
||||
result := sr.gormClient.WithContext(ctx).
|
||||
Model(models.ResourceFhir{}).
|
||||
Select("source_id, source_resource_type as resource_type, count(*) as count").
|
||||
Group("source_resource_type").
|
||||
Where(models.OriginBase{
|
||||
UserID: sr.GetCurrentUser(ctx).ID,
|
||||
SourceID: sourceUUID,
|
||||
}).
|
||||
Scan(&resourceTypeCounts)
|
||||
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
|
||||
sourceSummary.ResourceTypeCounts = resourceTypeCounts
|
||||
|
||||
//set patient
|
||||
var wrappedPatientResourceModel models.ResourceFhir
|
||||
results := sr.gormClient.WithContext(ctx).
|
||||
Where(models.OriginBase{
|
||||
UserID: sr.GetCurrentUser(ctx).ID,
|
||||
SourceResourceType: "Patient",
|
||||
SourceID: sourceUUID,
|
||||
}).
|
||||
First(&wrappedPatientResourceModel)
|
||||
|
||||
if results.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
sourceSummary.Patient = &wrappedPatientResourceModel
|
||||
|
||||
return sourceSummary, nil
|
||||
}
|
||||
|
||||
func (sr *sqliteRepository) GetSources(ctx context.Context) ([]models.Source, error) {
|
||||
|
||||
var sourceCreds []models.Source
|
||||
results := sr.gormClient.WithContext(ctx).
|
||||
Where(models.Source{UserID: sr.GetCurrentUser(ctx).ID}).
|
||||
Find(&sourceCreds)
|
||||
|
||||
return sourceCreds, results.Error
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Utilities
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func sqlitePragmaString(pragmas map[string]string) string {
|
||||
q := url.Values{}
|
||||
for key, val := range pragmas {
|
||||
q.Add("_pragma", key+"="+val)
|
||||
}
|
||||
|
||||
queryStr := q.Encode()
|
||||
if len(queryStr) > 0 {
|
||||
return "?" + queryStr
|
||||
}
|
||||
return ""
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
# Hub
|
||||
|
||||
The Fasten Hub is a semi stand-alone application that can retrieve data from various Medical Providers, transform it, and
|
||||
store it in the database. This code will eventually be moved into its own repository.
|
||||
|
||||
# Types
|
||||
|
||||
There are multiple protocols used by the Medical Provider industry to transfer patient data, the following mechanisms are the
|
||||
ones that Fasten supports
|
||||
|
||||
### FHIR
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
package hub
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/hub/internal/fhir/aetna"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/hub/internal/fhir/athena"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/hub/internal/fhir/base"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/hub/internal/fhir/bluebutton"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/hub/internal/fhir/careevolution"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/hub/internal/fhir/cerner"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/hub/internal/fhir/cigna"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/hub/internal/fhir/epic"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/hub/internal/fhir/healthit"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/hub/internal/fhir/logica"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/hub/internal/fhir/manual"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func NewClient(sourceType pkg.SourceType, ctx context.Context, appConfig config.Interface, globalLogger logrus.FieldLogger, credentials models.Source, testHttpClient ...*http.Client) (base.Client, *models.Source, error) {
|
||||
|
||||
var sourceClient base.Client
|
||||
var updatedSource *models.Source
|
||||
var err error
|
||||
switch sourceType {
|
||||
case pkg.SourceTypeAetna:
|
||||
sourceClient, updatedSource, err = aetna.NewClient(ctx, appConfig, globalLogger, credentials, testHttpClient...)
|
||||
case pkg.SourceTypeAthena:
|
||||
sourceClient, updatedSource, err = athena.NewClient(ctx, appConfig, globalLogger, credentials, testHttpClient...)
|
||||
case pkg.SourceTypeAnthem:
|
||||
sourceClient, updatedSource, err = cigna.NewClient(ctx, appConfig, globalLogger, credentials, testHttpClient...)
|
||||
case pkg.SourceTypeBlueButtonMedicare:
|
||||
sourceClient, updatedSource, err = bluebutton.NewClient(ctx, appConfig, globalLogger, credentials, testHttpClient...)
|
||||
case pkg.SourceTypeCareEvolution:
|
||||
sourceClient, updatedSource, err = careevolution.NewClient(ctx, appConfig, globalLogger, credentials, testHttpClient...)
|
||||
case pkg.SourceTypeCerner:
|
||||
sourceClient, updatedSource, err = cerner.NewClient(ctx, appConfig, globalLogger, credentials, testHttpClient...)
|
||||
case pkg.SourceTypeCigna:
|
||||
sourceClient, updatedSource, err = cigna.NewClient(ctx, appConfig, globalLogger, credentials, testHttpClient...)
|
||||
case pkg.SourceTypeEpic:
|
||||
sourceClient, updatedSource, err = epic.NewClient(ctx, appConfig, globalLogger, credentials, testHttpClient...)
|
||||
case pkg.SourceTypeHealthIT:
|
||||
sourceClient, updatedSource, err = healthit.NewClient(ctx, appConfig, globalLogger, credentials, testHttpClient...)
|
||||
case pkg.SourceTypeLogica:
|
||||
sourceClient, updatedSource, err = logica.NewClient(ctx, appConfig, globalLogger, credentials, testHttpClient...)
|
||||
case pkg.SourceTypeManual:
|
||||
sourceClient, updatedSource, err = manual.NewClient(ctx, appConfig, globalLogger, credentials, testHttpClient...)
|
||||
default:
|
||||
return nil, updatedSource, errors.New(fmt.Sprintf("Unknown Source Type: %s", sourceType))
|
||||
}
|
||||
|
||||
return sourceClient, updatedSource, err
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
package aetna
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/hub/internal/fhir/base"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type AetnaClient struct {
|
||||
*base.FHIR401Client
|
||||
}
|
||||
|
||||
func NewClient(ctx context.Context, appConfig config.Interface, globalLogger logrus.FieldLogger, source models.Source, testHttpClient ...*http.Client) (base.Client, *models.Source, error) {
|
||||
baseClient, updatedSource, err := base.NewFHIR401Client(ctx, appConfig, globalLogger, source, testHttpClient...)
|
||||
return AetnaClient{
|
||||
baseClient,
|
||||
}, updatedSource, err
|
||||
}
|
||||
|
||||
//Overrides
|
||||
|
||||
func (c AetnaClient) SyncAll(db database.DatabaseRepository) error {
|
||||
|
||||
bundle, err := c.GetResourceBundle("Patient")
|
||||
if err != nil {
|
||||
c.Logger.Infof("An error occurred while getting patient bundle %s", c.Source.PatientId)
|
||||
return err
|
||||
}
|
||||
|
||||
wrappedResourceModels, err := c.ProcessBundle(bundle)
|
||||
if err != nil {
|
||||
c.Logger.Infof("An error occurred while processing patient bundle %s", c.Source.PatientId)
|
||||
return err
|
||||
}
|
||||
//todo, create the resources in dependency order
|
||||
|
||||
for _, apiModel := range wrappedResourceModels {
|
||||
err = db.UpsertResource(context.Background(), &apiModel)
|
||||
if err != nil {
|
||||
c.Logger.Info("An error occurred while upserting resource")
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
package athena
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/hub/internal/fhir/base"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type AthenaClient struct {
|
||||
*base.FHIR401Client
|
||||
}
|
||||
|
||||
func NewClient(ctx context.Context, appConfig config.Interface, globalLogger logrus.FieldLogger, source models.Source, testHttpClient ...*http.Client) (base.Client, *models.Source, error) {
|
||||
baseClient, updatedSource, err := base.NewFHIR401Client(ctx, appConfig, globalLogger, source, testHttpClient...)
|
||||
return AthenaClient{
|
||||
baseClient,
|
||||
}, updatedSource, err
|
||||
}
|
||||
|
||||
func (c AthenaClient) SyncAll(db database.DatabaseRepository) error {
|
||||
supportedResources := []string{
|
||||
"AllergyIntolerance",
|
||||
//"Binary",
|
||||
"CarePlan",
|
||||
"CareTeam",
|
||||
"Condition",
|
||||
"Device",
|
||||
"DiagnosticReport",
|
||||
"DocumentReference",
|
||||
"Encounter",
|
||||
"Goal",
|
||||
"Immunization",
|
||||
//"Location",
|
||||
//"Medication",
|
||||
//"MedicationRequest",
|
||||
"Observation",
|
||||
//"Organization",
|
||||
//"Patient",
|
||||
//"Practitioner",
|
||||
"Procedure",
|
||||
//"Provenance",
|
||||
}
|
||||
|
||||
return c.SyncAllByResourceName(db, supportedResources)
|
||||
}
|
|
@ -1,154 +0,0 @@
|
|||
package base
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/oauth2"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type BaseClient struct {
|
||||
Context context.Context
|
||||
AppConfig config.Interface
|
||||
Logger logrus.FieldLogger
|
||||
|
||||
OauthClient *http.Client
|
||||
Source models.Source
|
||||
Headers map[string]string
|
||||
}
|
||||
|
||||
func (c *BaseClient) SyncAllBundle(db database.DatabaseRepository, bundleFile *os.File) error {
|
||||
panic("SyncAllBundle functionality is not available on this client")
|
||||
}
|
||||
|
||||
func NewBaseClient(ctx context.Context, appConfig config.Interface, globalLogger logrus.FieldLogger, source models.Source, testHttpClient ...*http.Client) (*BaseClient, *models.Source, error) {
|
||||
var httpClient *http.Client
|
||||
var updatedSource *models.Source
|
||||
if len(testHttpClient) == 0 {
|
||||
//check if we need to refresh the access token
|
||||
//https://github.com/golang/oauth2/issues/84#issuecomment-520099526
|
||||
// https://chromium.googlesource.com/external/github.com/golang/oauth2/+/8f816d62a2652f705144857bbbcc26f2c166af9e/oauth2.go#239
|
||||
conf := &oauth2.Config{
|
||||
ClientID: source.ClientId,
|
||||
ClientSecret: "",
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: source.OauthAuthorizationEndpoint,
|
||||
TokenURL: source.OauthTokenEndpoint,
|
||||
},
|
||||
//RedirectURL: "",
|
||||
//Scopes: nil,
|
||||
}
|
||||
token := &oauth2.Token{
|
||||
TokenType: "Bearer",
|
||||
RefreshToken: source.RefreshToken,
|
||||
AccessToken: source.AccessToken,
|
||||
Expiry: time.Unix(source.ExpiresAt, 0),
|
||||
}
|
||||
if token.Expiry.Before(time.Now()) { // expired so let's update it
|
||||
log.Println("access token expired, refreshing...")
|
||||
src := conf.TokenSource(ctx, token)
|
||||
newToken, err := src.Token() // this actually goes and renews the tokens
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if newToken.AccessToken != token.AccessToken {
|
||||
token = newToken
|
||||
|
||||
// update the "source" credential with new data (which will need to be sent
|
||||
updatedSource = &source
|
||||
updatedSource.AccessToken = newToken.AccessToken
|
||||
updatedSource.ExpiresAt = newToken.Expiry.Unix()
|
||||
// Don't overwrite `RefreshToken` with an empty value
|
||||
// if this was a token refreshing request.
|
||||
if newToken.RefreshToken != "" {
|
||||
updatedSource.RefreshToken = newToken.RefreshToken
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// OLD CODE
|
||||
httpClient = oauth2.NewClient(ctx, oauth2.StaticTokenSource(token))
|
||||
|
||||
} else {
|
||||
//Testing mode.
|
||||
httpClient = testHttpClient[0]
|
||||
}
|
||||
|
||||
httpClient.Timeout = 10 * time.Second
|
||||
|
||||
return &BaseClient{
|
||||
Context: ctx,
|
||||
AppConfig: appConfig,
|
||||
Logger: globalLogger,
|
||||
OauthClient: httpClient,
|
||||
Source: source,
|
||||
Headers: map[string]string{},
|
||||
}, updatedSource, nil
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// HttpClient
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
func (c *BaseClient) GetRequest(resourceSubpathOrNext string, decodeModelPtr interface{}) error {
|
||||
resourceUrl, err := url.Parse(resourceSubpathOrNext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !resourceUrl.IsAbs() {
|
||||
resourceUrl, err = url.Parse(fmt.Sprintf("%s/%s", strings.TrimRight(c.Source.ApiEndpointBaseUrl, "/"), strings.TrimLeft(resourceSubpathOrNext, "/")))
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, resourceUrl.String(), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for key, val := range c.Headers {
|
||||
//req.Header.Add("Accept", "application/json+fhir")
|
||||
req.Header.Add(key, val)
|
||||
}
|
||||
|
||||
//resp, err := c.OauthClient.Get(url)
|
||||
resp, err := c.OauthClient.Do(req)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode >= 300 || resp.StatusCode < 200 {
|
||||
b, _ := io.ReadAll(resp.Body)
|
||||
return fmt.Errorf("An error occurred during request %s - %d - %s [%s]", resourceUrl, resp.StatusCode, resp.Status, string(b))
|
||||
}
|
||||
|
||||
err = ParseBundle(resp.Body, decodeModelPtr)
|
||||
return err
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Helper Functions
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
func ParseBundle(r io.Reader, decodeModelPtr interface{}) error {
|
||||
decoder := json.NewDecoder(r)
|
||||
//decoder.DisallowUnknownFields() //make sure we throw an error if unknown fields are present.
|
||||
err := decoder.Decode(decodeModelPtr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
|
@ -1,208 +0,0 @@
|
|||
package base
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"github.com/fastenhealth/gofhir-models/fhir401"
|
||||
fhirutils "github.com/fastenhealth/gofhir-models/fhir401/utils"
|
||||
"github.com/samber/lo"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gorm.io/datatypes"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type FHIR401Client struct {
|
||||
*BaseClient
|
||||
}
|
||||
|
||||
func NewFHIR401Client(ctx context.Context, appConfig config.Interface, globalLogger logrus.FieldLogger, source models.Source, testHttpClient ...*http.Client) (*FHIR401Client, *models.Source, error) {
|
||||
baseClient, updatedSource, err := NewBaseClient(ctx, appConfig, globalLogger, source, testHttpClient...)
|
||||
return &FHIR401Client{
|
||||
baseClient,
|
||||
}, updatedSource, err
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Sync
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
func (c *FHIR401Client) SyncAll(db database.DatabaseRepository) error {
|
||||
|
||||
bundle, err := c.GetPatientBundle(c.Source.PatientId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wrappedResourceModels, err := c.ProcessBundle(bundle)
|
||||
if err != nil {
|
||||
c.Logger.Infof("An error occurred while processing patient bundle %s", c.Source.PatientId)
|
||||
return err
|
||||
}
|
||||
//todo, create the resources in dependency order
|
||||
|
||||
for _, apiModel := range wrappedResourceModels {
|
||||
err = db.UpsertResource(context.Background(), &apiModel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//TODO, find a way to sync references that cannot be searched by patient ID.
|
||||
func (c *FHIR401Client) SyncAllByResourceName(db database.DatabaseRepository, resourceNames []string) error {
|
||||
|
||||
//Store the Patient
|
||||
patientResource, err := c.GetPatient(c.Source.PatientId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
patientJson, err := patientResource.MarshalJSON()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
patientResourceType, patientResourceId := patientResource.ResourceRef()
|
||||
patientResourceFhir := models.ResourceFhir{
|
||||
OriginBase: models.OriginBase{
|
||||
UserID: c.Source.UserID,
|
||||
SourceID: c.Source.ID,
|
||||
SourceResourceType: patientResourceType,
|
||||
SourceResourceID: *patientResourceId,
|
||||
},
|
||||
Payload: datatypes.JSON(patientJson),
|
||||
}
|
||||
db.UpsertResource(context.Background(), &patientResourceFhir)
|
||||
|
||||
//error map storage.
|
||||
syncErrors := map[string]error{}
|
||||
|
||||
//Store all other resources.
|
||||
for _, resourceType := range resourceNames {
|
||||
bundle, err := c.GetResourceBundle(fmt.Sprintf("%s?patient=%s", resourceType, c.Source.PatientId))
|
||||
if err != nil {
|
||||
syncErrors[resourceType] = err
|
||||
continue
|
||||
}
|
||||
wrappedResourceModels, err := c.ProcessBundle(bundle)
|
||||
if err != nil {
|
||||
c.Logger.Infof("An error occurred while processing %s bundle %s", resourceType, c.Source.PatientId)
|
||||
syncErrors[resourceType] = err
|
||||
continue
|
||||
}
|
||||
for _, apiModel := range wrappedResourceModels {
|
||||
err = db.UpsertResource(context.Background(), &apiModel)
|
||||
if err != nil {
|
||||
syncErrors[resourceType] = err
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(syncErrors) > 0 {
|
||||
return fmt.Errorf("%d error(s) occurred during sync: %v", len(syncErrors), syncErrors)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// FHIR
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
func (c *FHIR401Client) GetResourceBundle(relativeResourcePath string) (fhir401.Bundle, error) {
|
||||
|
||||
// https://www.hl7.org/fhir/patient-operation-everything.html
|
||||
bundle := fhir401.Bundle{}
|
||||
err := c.GetRequest(relativeResourcePath, &bundle)
|
||||
if err != nil {
|
||||
return bundle, err
|
||||
}
|
||||
var next string
|
||||
var prev string
|
||||
var self string
|
||||
for _, link := range bundle.Link {
|
||||
if link.Relation == "next" {
|
||||
next = link.Url
|
||||
} else if link.Relation == "self" {
|
||||
self = link.Url
|
||||
} else if link.Relation == "previous" {
|
||||
prev = link.Url
|
||||
}
|
||||
}
|
||||
|
||||
for len(next) > 0 && next != self && next != prev {
|
||||
c.Logger.Debugf("Paginated request => %s", next)
|
||||
nextBundle := fhir401.Bundle{}
|
||||
err := c.GetRequest(next, &nextBundle)
|
||||
if err != nil {
|
||||
return bundle, nil //ignore failures when paginating?
|
||||
}
|
||||
bundle.Entry = append(bundle.Entry, nextBundle.Entry...)
|
||||
|
||||
next = "" //reset the pointers
|
||||
self = ""
|
||||
prev = ""
|
||||
for _, link := range nextBundle.Link {
|
||||
if link.Relation == "next" {
|
||||
next = link.Url
|
||||
} else if link.Relation == "self" {
|
||||
self = link.Url
|
||||
} else if link.Relation == "previous" {
|
||||
prev = link.Url
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bundle, err
|
||||
|
||||
}
|
||||
|
||||
func (c *FHIR401Client) GetPatientBundle(patientId string) (fhir401.Bundle, error) {
|
||||
return c.GetResourceBundle(fmt.Sprintf("Patient/%s/$everything", patientId))
|
||||
}
|
||||
|
||||
func (c *FHIR401Client) GetPatient(patientId string) (fhir401.Patient, error) {
|
||||
|
||||
patient := fhir401.Patient{}
|
||||
err := c.GetRequest(fmt.Sprintf("Patient/%s", patientId), &patient)
|
||||
return patient, err
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Process Bundles
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
func (c *FHIR401Client) ProcessBundle(bundle fhir401.Bundle) ([]models.ResourceFhir, error) {
|
||||
|
||||
//process each entry in bundle
|
||||
wrappedResourceModels := lo.FilterMap[fhir401.BundleEntry, models.ResourceFhir](bundle.Entry, func(bundleEntry fhir401.BundleEntry, _ int) (models.ResourceFhir, bool) {
|
||||
originalResource, _ := fhirutils.MapToResource(bundleEntry.Resource, false)
|
||||
|
||||
resourceType, resourceId := originalResource.(ResourceInterface).ResourceRef()
|
||||
|
||||
// TODO find a way to safely/consistently get the resource updated date (and other metadata) which shoudl be added to the model.
|
||||
//if originalResource.Meta != nil && originalResource.Meta.LastUpdated != nil {
|
||||
// if parsed, err := time.Parse(time.RFC3339Nano, *originalResource.Meta.LastUpdated); err == nil {
|
||||
// patientProfile.UpdatedAt = parsed
|
||||
// }
|
||||
//}
|
||||
if resourceId == nil {
|
||||
//no resourceId present for this resource, we'll ignore it.
|
||||
return models.ResourceFhir{}, false
|
||||
}
|
||||
|
||||
wrappedResourceModel := models.ResourceFhir{
|
||||
OriginBase: models.OriginBase{
|
||||
ModelBase: models.ModelBase{},
|
||||
UserID: c.Source.UserID,
|
||||
SourceID: c.Source.ID,
|
||||
SourceResourceID: *resourceId,
|
||||
SourceResourceType: resourceType,
|
||||
},
|
||||
Payload: datatypes.JSON(bundleEntry.Resource),
|
||||
}
|
||||
|
||||
return wrappedResourceModel, true
|
||||
})
|
||||
return wrappedResourceModels, nil
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
package base
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
mock_config "github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config/mock"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"github.com/fastenhealth/gofhir-models/fhir401"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/require"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// helpers
|
||||
func readTestFixture(path string) ([]byte, error) {
|
||||
jsonFile, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer jsonFile.Close()
|
||||
return ioutil.ReadAll(jsonFile)
|
||||
}
|
||||
|
||||
func TestNewFHIR401Client(t *testing.T) {
|
||||
t.Parallel()
|
||||
//setup
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
fakeConfig := mock_config.NewMockInterface(mockCtrl)
|
||||
|
||||
testLogger := logrus.WithFields(logrus.Fields{
|
||||
"type": "test",
|
||||
})
|
||||
|
||||
//test
|
||||
client, _, err := NewFHIR401Client(context.Background(), fakeConfig, testLogger, models.Source{
|
||||
RefreshToken: "test-refresh-token",
|
||||
AccessToken: "test-access-token",
|
||||
}, &http.Client{})
|
||||
|
||||
//assert
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, client.Source.AccessToken, "test-access-token")
|
||||
require.Equal(t, client.Source.RefreshToken, "test-refresh-token")
|
||||
}
|
||||
|
||||
func TestFHIR401Client_ProcessBundle(t *testing.T) {
|
||||
t.Parallel()
|
||||
//setup
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
fakeConfig := mock_config.NewMockInterface(mockCtrl)
|
||||
testLogger := logrus.WithFields(logrus.Fields{
|
||||
"type": "test",
|
||||
})
|
||||
client, _, err := NewFHIR401Client(context.Background(), fakeConfig, testLogger, models.Source{
|
||||
RefreshToken: "test-refresh-token",
|
||||
AccessToken: "test-access-token",
|
||||
}, &http.Client{})
|
||||
require.NoError(t, err)
|
||||
|
||||
jsonBytes, err := readTestFixture("testdata/fixtures/401-R4/bundle/cigna_syntheticuser05-everything.json")
|
||||
require.NoError(t, err)
|
||||
var bundle fhir401.Bundle
|
||||
err = json.Unmarshal(jsonBytes, &bundle)
|
||||
require.NoError(t, err)
|
||||
|
||||
// test
|
||||
wrappedResourceModels, err := client.ProcessBundle(bundle)
|
||||
//log.Printf("%v", wrappedResourceModels)
|
||||
|
||||
//assert
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 11, len(wrappedResourceModels))
|
||||
//require.Equal(t, "A00000000000005", profile.SourceResourceID)
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
package base
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"github.com/fastenhealth/gofhir-models/fhir430"
|
||||
fhirutils "github.com/fastenhealth/gofhir-models/fhir430/utils"
|
||||
"github.com/samber/lo"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gorm.io/datatypes"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type FHIR430Client struct {
|
||||
*BaseClient
|
||||
}
|
||||
|
||||
func NewFHIR430Client(ctx context.Context, appConfig config.Interface, globalLogger logrus.FieldLogger, source models.Source, testHttpClient ...*http.Client) (*FHIR430Client, *models.Source, error) {
|
||||
baseClient, updatedSource, err := NewBaseClient(ctx, appConfig, globalLogger, source, testHttpClient...)
|
||||
return &FHIR430Client{
|
||||
baseClient,
|
||||
}, updatedSource, err
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// FHIR
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
func (c *FHIR430Client) GetPatientEverything(patientId string) (*fhir430.Bundle, error) {
|
||||
|
||||
// https://www.hl7.org/fhir/patient-operation-everything.html
|
||||
bundle := fhir430.Bundle{}
|
||||
err := c.GetRequest(fmt.Sprintf("Patient/%s/$everything", patientId), &bundle)
|
||||
return &bundle, err
|
||||
}
|
||||
|
||||
func (c *FHIR430Client) GetPatient(patientId string) (*fhir430.Patient, error) {
|
||||
|
||||
patient := fhir430.Patient{}
|
||||
err := c.GetRequest(fmt.Sprintf("Patient/%s", patientId), &patient)
|
||||
return &patient, err
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Process Bundles
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
func (c *FHIR430Client) ProcessBundle(bundle fhir430.Bundle) ([]models.ResourceFhir, error) {
|
||||
|
||||
//process each entry in bundle
|
||||
wrappedResourceModels := lo.FilterMap[fhir430.BundleEntry, models.ResourceFhir](bundle.Entry, func(bundleEntry fhir430.BundleEntry, _ int) (models.ResourceFhir, bool) {
|
||||
originalResource, _ := fhirutils.MapToResource(bundleEntry.Resource, false)
|
||||
|
||||
resourceType, resourceId := originalResource.(ResourceInterface).ResourceRef()
|
||||
|
||||
// TODO find a way to safely/consistently get the resource updated date (and other metadata) which shoudl be added to the model.
|
||||
//if originalResource.Meta != nil && originalResource.Meta.LastUpdated != nil {
|
||||
// if parsed, err := time.Parse(time.RFC3339Nano, *originalResource.Meta.LastUpdated); err == nil {
|
||||
// patientProfile.UpdatedAt = parsed
|
||||
// }
|
||||
//}
|
||||
if resourceId == nil {
|
||||
//no resourceId present for this resource, we'll ignore it.
|
||||
return models.ResourceFhir{}, false
|
||||
}
|
||||
|
||||
wrappedResourceModel := models.ResourceFhir{
|
||||
OriginBase: models.OriginBase{
|
||||
ModelBase: models.ModelBase{},
|
||||
UserID: c.Source.UserID,
|
||||
SourceID: c.Source.ID,
|
||||
SourceResourceID: *resourceId,
|
||||
SourceResourceType: resourceType,
|
||||
},
|
||||
Payload: datatypes.JSON(bundleEntry.Resource),
|
||||
}
|
||||
|
||||
return wrappedResourceModel, true
|
||||
})
|
||||
return wrappedResourceModels, nil
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package base
|
||||
|
||||
import (
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database"
|
||||
"os"
|
||||
)
|
||||
|
||||
//go:generate mockgen -source=interface.go -destination=mock/mock_client.go
|
||||
type Client interface {
|
||||
GetRequest(resourceSubpath string, decodeModelPtr interface{}) error
|
||||
SyncAll(db database.DatabaseRepository) error
|
||||
|
||||
//Manual client ONLY functions
|
||||
SyncAllBundle(db database.DatabaseRepository, bundleFile *os.File) error
|
||||
}
|
||||
|
||||
type ResourceInterface interface {
|
||||
ResourceRef() (string, *string)
|
||||
}
|
|
@ -1,116 +0,0 @@
|
|||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: interface.go
|
||||
|
||||
// Package mock_base is a generated GoMock package.
|
||||
package mock_base
|
||||
|
||||
import (
|
||||
os "os"
|
||||
reflect "reflect"
|
||||
|
||||
database "github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
)
|
||||
|
||||
// MockClient is a mock of Client interface.
|
||||
type MockClient struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockClientMockRecorder
|
||||
}
|
||||
|
||||
// MockClientMockRecorder is the mock recorder for MockClient.
|
||||
type MockClientMockRecorder struct {
|
||||
mock *MockClient
|
||||
}
|
||||
|
||||
// NewMockClient creates a new mock instance.
|
||||
func NewMockClient(ctrl *gomock.Controller) *MockClient {
|
||||
mock := &MockClient{ctrl: ctrl}
|
||||
mock.recorder = &MockClientMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockClient) EXPECT() *MockClientMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// GetRequest mocks base method.
|
||||
func (m *MockClient) GetRequest(resourceSubpath string, decodeModelPtr interface{}) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetRequest", resourceSubpath, decodeModelPtr)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// GetRequest indicates an expected call of GetRequest.
|
||||
func (mr *MockClientMockRecorder) GetRequest(resourceSubpath, decodeModelPtr interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRequest", reflect.TypeOf((*MockClient)(nil).GetRequest), resourceSubpath, decodeModelPtr)
|
||||
}
|
||||
|
||||
// SyncAll mocks base method.
|
||||
func (m *MockClient) SyncAll(db database.DatabaseRepository) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "SyncAll", db)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// SyncAll indicates an expected call of SyncAll.
|
||||
func (mr *MockClientMockRecorder) SyncAll(db interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SyncAll", reflect.TypeOf((*MockClient)(nil).SyncAll), db)
|
||||
}
|
||||
|
||||
// SyncAllBundle mocks base method.
|
||||
func (m *MockClient) SyncAllBundle(db database.DatabaseRepository, bundleFile *os.File) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "SyncAllBundle", db, bundleFile)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// SyncAllBundle indicates an expected call of SyncAllBundle.
|
||||
func (mr *MockClientMockRecorder) SyncAllBundle(db, bundleFile interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SyncAllBundle", reflect.TypeOf((*MockClient)(nil).SyncAllBundle), db, bundleFile)
|
||||
}
|
||||
|
||||
// MockResourceInterface is a mock of ResourceInterface interface.
|
||||
type MockResourceInterface struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockResourceInterfaceMockRecorder
|
||||
}
|
||||
|
||||
// MockResourceInterfaceMockRecorder is the mock recorder for MockResourceInterface.
|
||||
type MockResourceInterfaceMockRecorder struct {
|
||||
mock *MockResourceInterface
|
||||
}
|
||||
|
||||
// NewMockResourceInterface creates a new mock instance.
|
||||
func NewMockResourceInterface(ctrl *gomock.Controller) *MockResourceInterface {
|
||||
mock := &MockResourceInterface{ctrl: ctrl}
|
||||
mock.recorder = &MockResourceInterfaceMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockResourceInterface) EXPECT() *MockResourceInterfaceMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// ResourceRef mocks base method.
|
||||
func (m *MockResourceInterface) ResourceRef() (string, *string) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ResourceRef")
|
||||
ret0, _ := ret[0].(string)
|
||||
ret1, _ := ret[1].(*string)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// ResourceRef indicates an expected call of ResourceRef.
|
||||
func (mr *MockResourceInterfaceMockRecorder) ResourceRef() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResourceRef", reflect.TypeOf((*MockResourceInterface)(nil).ResourceRef))
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
package base
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"github.com/seborama/govcr"
|
||||
"golang.org/x/oauth2"
|
||||
"net/http"
|
||||
"path"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func OAuthVcrSetup(t *testing.T, enableRecording bool) *http.Client {
|
||||
accessToken := "PLACEHOLDER"
|
||||
if enableRecording {
|
||||
//this has to be disabled because CI is empty inside docker containers.
|
||||
accessToken = ""
|
||||
}
|
||||
|
||||
ts := oauth2.StaticTokenSource(
|
||||
//setting a real access token here will allow API calls to connect successfully
|
||||
&oauth2.Token{AccessToken: accessToken},
|
||||
)
|
||||
|
||||
tr := http.DefaultTransport.(*http.Transport)
|
||||
tr.TLSClientConfig = &tls.Config{
|
||||
InsecureSkipVerify: true, //disable certificate validation because we're playing back http requests.
|
||||
}
|
||||
insecureClient := http.Client{
|
||||
Transport: tr,
|
||||
}
|
||||
|
||||
ctx := context.WithValue(oauth2.NoContext, oauth2.HTTPClient, insecureClient)
|
||||
tc := oauth2.NewClient(ctx, ts)
|
||||
|
||||
vcrConfig := govcr.VCRConfig{
|
||||
Logging: true,
|
||||
CassettePath: path.Join("testdata", "govcr-fixtures"),
|
||||
Client: tc,
|
||||
|
||||
//this line ensures that we do not attempt to create new recordings.
|
||||
//Comment this out if you would like to make recordings.
|
||||
DisableRecording: !enableRecording,
|
||||
}
|
||||
|
||||
// HTTP headers are case-insensitive
|
||||
vcrConfig.RequestFilters.Add(govcr.RequestDeleteHeaderKeys("User-Agent", "user-agent"))
|
||||
|
||||
vcr := govcr.NewVCR(t.Name(), &vcrConfig)
|
||||
return vcr.Client
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,31 +0,0 @@
|
|||
package bluebutton
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/hub/internal/fhir/base"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type BlueButtonClient struct {
|
||||
*base.FHIR401Client
|
||||
}
|
||||
|
||||
func NewClient(ctx context.Context, appConfig config.Interface, globalLogger logrus.FieldLogger, source models.Source, testHttpClient ...*http.Client) (base.Client, *models.Source, error) {
|
||||
baseClient, updatedSource, err := base.NewFHIR401Client(ctx, appConfig, globalLogger, source, testHttpClient...)
|
||||
return BlueButtonClient{
|
||||
baseClient,
|
||||
}, updatedSource, err
|
||||
}
|
||||
|
||||
func (c BlueButtonClient) SyncAll(db database.DatabaseRepository) error {
|
||||
|
||||
supportedResources := []string{
|
||||
"ExplanationOfBenefit",
|
||||
"Coverage",
|
||||
}
|
||||
return c.SyncAllByResourceName(db, supportedResources)
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
package careevolution
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/hub/internal/fhir/base"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type CareEvolutionClient struct {
|
||||
*base.FHIR401Client
|
||||
}
|
||||
|
||||
func NewClient(ctx context.Context, appConfig config.Interface, globalLogger logrus.FieldLogger, source models.Source, testHttpClient ...*http.Client) (base.Client, *models.Source, error) {
|
||||
baseClient, updatedSource, err := base.NewFHIR401Client(ctx, appConfig, globalLogger, source, testHttpClient...)
|
||||
baseClient.Headers["Accept"] = "application/json+fhir"
|
||||
return CareEvolutionClient{
|
||||
baseClient,
|
||||
}, updatedSource, err
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
package cerner
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/hub/internal/fhir/base"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type CernerClient struct {
|
||||
*base.FHIR401Client
|
||||
}
|
||||
|
||||
func NewClient(ctx context.Context, appConfig config.Interface, globalLogger logrus.FieldLogger, source models.Source, testHttpClient ...*http.Client) (base.Client, *models.Source, error) {
|
||||
baseClient, updatedSource, err := base.NewFHIR401Client(ctx, appConfig, globalLogger, source, testHttpClient...)
|
||||
baseClient.Headers["Accept"] = "application/json+fhir"
|
||||
return CernerClient{
|
||||
baseClient,
|
||||
}, updatedSource, err
|
||||
}
|
||||
|
||||
func (c CernerClient) SyncAll(db database.DatabaseRepository) error {
|
||||
|
||||
supportedResources := []string{
|
||||
"AllergyIntolerance",
|
||||
"CarePlan",
|
||||
"CareTeam",
|
||||
"Condition",
|
||||
"Consent",
|
||||
"Device",
|
||||
"Encounter",
|
||||
"FamilyMemberHistory",
|
||||
"Goal",
|
||||
"Immunization",
|
||||
"InsurancePlan",
|
||||
"MedicationRequest",
|
||||
"NutritionOrder",
|
||||
"Observation",
|
||||
"Person",
|
||||
"Procedure",
|
||||
"Provenance",
|
||||
"Questionnaire",
|
||||
"QuestionnaireResponse",
|
||||
"RelatedPerson",
|
||||
"Schedule",
|
||||
"ServiceRequest",
|
||||
"Slot",
|
||||
}
|
||||
return c.SyncAllByResourceName(db, supportedResources)
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
package cigna
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/hub/internal/fhir/base"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type CignaClient struct {
|
||||
*base.FHIR401Client
|
||||
}
|
||||
|
||||
func NewClient(ctx context.Context, appConfig config.Interface, globalLogger logrus.FieldLogger, source models.Source, testHttpClient ...*http.Client) (base.Client, *models.Source, error) {
|
||||
baseClient, updatedSource, err := base.NewFHIR401Client(ctx, appConfig, globalLogger, source, testHttpClient...)
|
||||
return CignaClient{
|
||||
baseClient,
|
||||
}, updatedSource, err
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
package cigna
|
||||
|
||||
import (
|
||||
"context"
|
||||
mock_config "github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config/mock"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/hub/internal/fhir/base"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/require"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCignaClient_SyncAll(t *testing.T) {
|
||||
t.Parallel()
|
||||
//setup
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
fakeConfig := mock_config.NewMockInterface(mockCtrl)
|
||||
|
||||
testDatabase, err := ioutil.TempFile("testdata", "fasten.db")
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(testDatabase.Name())
|
||||
fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(testDatabase.Name())
|
||||
testLogger := logrus.WithFields(logrus.Fields{
|
||||
"type": "test",
|
||||
})
|
||||
httpClient := base.OAuthVcrSetup(t, false)
|
||||
client, _, err := NewClient(context.Background(), fakeConfig, testLogger, models.Source{
|
||||
SourceType: "cigna",
|
||||
PatientId: "A00000000000005",
|
||||
ApiEndpointBaseUrl: "https://p-hi2.digitaledge.cigna.com/PatientAccess/v1-devportal",
|
||||
ClientId: "e434426c-2aaf-413a-a39a-8f5f6130f287",
|
||||
}, httpClient)
|
||||
|
||||
db, err := database.NewRepository(fakeConfig, testLogger)
|
||||
require.NoError(t, err)
|
||||
|
||||
//test
|
||||
err = client.SyncAll(db)
|
||||
require.NoError(t, err)
|
||||
|
||||
//assert
|
||||
require.NoError(t, err)
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -1,54 +0,0 @@
|
|||
package epic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/hub/internal/fhir/base"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type EpicClient struct {
|
||||
*base.FHIR401Client
|
||||
}
|
||||
|
||||
func NewClient(ctx context.Context, appConfig config.Interface, globalLogger logrus.FieldLogger, source models.Source, testHttpClient ...*http.Client) (base.Client, *models.Source, error) {
|
||||
baseClient, updatedSource, err := base.NewFHIR401Client(ctx, appConfig, globalLogger, source, testHttpClient...)
|
||||
baseClient.Headers["Accept"] = "application/json+fhir"
|
||||
|
||||
return EpicClient{
|
||||
baseClient,
|
||||
}, updatedSource, err
|
||||
}
|
||||
|
||||
func (c EpicClient) SyncAll(db database.DatabaseRepository) error {
|
||||
|
||||
supportedResources := []string{
|
||||
"AllergyIntolerance",
|
||||
"CarePlan",
|
||||
"CareTeam",
|
||||
"Condition",
|
||||
"Consent",
|
||||
"Device",
|
||||
"Encounter",
|
||||
"FamilyMemberHistory",
|
||||
"Goal",
|
||||
"Immunization",
|
||||
"InsurancePlan",
|
||||
"MedicationRequest",
|
||||
"NutritionOrder",
|
||||
"Observation",
|
||||
"Person",
|
||||
"Procedure",
|
||||
"Provenance",
|
||||
"Questionnaire",
|
||||
"QuestionnaireResponse",
|
||||
"RelatedPerson",
|
||||
"Schedule",
|
||||
"ServiceRequest",
|
||||
"Slot",
|
||||
}
|
||||
return c.SyncAllByResourceName(db, supportedResources)
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
package healthit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/hub/internal/fhir/base"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type HealthItClient struct {
|
||||
*base.FHIR401Client
|
||||
}
|
||||
|
||||
func NewClient(ctx context.Context, appConfig config.Interface, globalLogger logrus.FieldLogger, source models.Source, testHttpClient ...*http.Client) (base.Client, *models.Source, error) {
|
||||
baseClient, updatedSource, err := base.NewFHIR401Client(ctx, appConfig, globalLogger, source, testHttpClient...)
|
||||
baseClient.Headers["Accept"] = "application/json+fhir"
|
||||
|
||||
return HealthItClient{
|
||||
baseClient,
|
||||
}, updatedSource, err
|
||||
}
|
||||
|
||||
func (c HealthItClient) SyncAll(db database.DatabaseRepository) error {
|
||||
|
||||
supportedResources := []string{
|
||||
"AllergyIntolerance",
|
||||
"CarePlan",
|
||||
"CareTeam",
|
||||
"Condition",
|
||||
"Consent",
|
||||
"Device",
|
||||
"Encounter",
|
||||
"FamilyMemberHistory",
|
||||
"Goal",
|
||||
"Immunization",
|
||||
"InsurancePlan",
|
||||
"MedicationRequest",
|
||||
"NutritionOrder",
|
||||
"Observation",
|
||||
"Person",
|
||||
"Procedure",
|
||||
"Provenance",
|
||||
"Questionnaire",
|
||||
"QuestionnaireResponse",
|
||||
"RelatedPerson",
|
||||
"Schedule",
|
||||
"ServiceRequest",
|
||||
"Slot",
|
||||
}
|
||||
return c.SyncAllByResourceName(db, supportedResources)
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
package logica
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/hub/internal/fhir/base"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type LogicaClient struct {
|
||||
*base.FHIR401Client
|
||||
}
|
||||
|
||||
func NewClient(ctx context.Context, appConfig config.Interface, globalLogger logrus.FieldLogger, source models.Source, testHttpClient ...*http.Client) (base.Client, *models.Source, error) {
|
||||
baseClient, updatedSource, err := base.NewFHIR401Client(ctx, appConfig, globalLogger, source, testHttpClient...)
|
||||
return LogicaClient{
|
||||
baseClient,
|
||||
}, updatedSource, err
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
package logica
|
||||
|
||||
import (
|
||||
"context"
|
||||
mock_config "github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config/mock"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/hub/internal/fhir/base"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/require"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLogicaClient_SyncAll(t *testing.T) {
|
||||
t.Parallel()
|
||||
//setup
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
fakeConfig := mock_config.NewMockInterface(mockCtrl)
|
||||
|
||||
testDatabase, err := ioutil.TempFile("", "fasten.db")
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(testDatabase.Name())
|
||||
fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(testDatabase.Name())
|
||||
testLogger := logrus.WithFields(logrus.Fields{
|
||||
"type": "test",
|
||||
})
|
||||
httpClient := base.OAuthVcrSetup(t, false)
|
||||
client, _, err := NewClient(context.Background(), fakeConfig, testLogger, models.Source{
|
||||
SourceType: "logica",
|
||||
PatientId: "smart-1288992",
|
||||
ApiEndpointBaseUrl: "https://api.logicahealth.org/fastenhealth/data",
|
||||
ClientId: "12b14c49-a4da-42f7-9e6f-2f19db622962",
|
||||
}, httpClient)
|
||||
require.NoError(t, err)
|
||||
|
||||
db, err := database.NewRepository(fakeConfig, testLogger)
|
||||
require.NoError(t, err)
|
||||
|
||||
//test
|
||||
err = client.SyncAll(db)
|
||||
require.NoError(t, err)
|
||||
|
||||
//assert
|
||||
require.NoError(t, err)
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -1,164 +0,0 @@
|
|||
package manual
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/hub/internal/fhir/base"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"github.com/fastenhealth/gofhir-models/fhir401"
|
||||
fhir401utils "github.com/fastenhealth/gofhir-models/fhir401/utils"
|
||||
"github.com/fastenhealth/gofhir-models/fhir430"
|
||||
fhir430utils "github.com/fastenhealth/gofhir-models/fhir430/utils"
|
||||
"github.com/samber/lo"
|
||||
"github.com/sirupsen/logrus"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ManualClient struct {
|
||||
Context context.Context
|
||||
AppConfig config.Interface
|
||||
Logger logrus.FieldLogger
|
||||
|
||||
Source *models.Source
|
||||
}
|
||||
|
||||
func (m ManualClient) GetRequest(resourceSubpath string, decodeModelPtr interface{}) error {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (m ManualClient) SyncAll(db database.DatabaseRepository) error {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (m ManualClient) SyncAllBundle(db database.DatabaseRepository, bundleFile *os.File) error {
|
||||
|
||||
// we need to find the (most populated) patient record
|
||||
patientId, bundleType, err := m.ExtractPatientId(bundleFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("an error occurred while extracting patient id from bundle: %w", err)
|
||||
}
|
||||
// we need to add the patient id to the source
|
||||
m.Source.PatientId = patientId
|
||||
|
||||
// we need to upsert Source
|
||||
err = db.CreateSource(m.Context, m.Source)
|
||||
if err != nil {
|
||||
return fmt.Errorf("an error occurred while creating manual source: %w", err)
|
||||
}
|
||||
// we need to parse the bundle into resources (might need to try a couple of different times)
|
||||
var resourceFhirList []models.ResourceFhir
|
||||
switch bundleType {
|
||||
case "fhir430":
|
||||
bundle430Data := fhir430.Bundle{}
|
||||
err := base.ParseBundle(bundleFile, &bundle430Data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("an error occurred while parsing 4.3.0 bundle: %w", err)
|
||||
}
|
||||
client, _, err := base.NewFHIR430Client(m.Context, m.AppConfig, m.Logger, *m.Source, http.DefaultClient)
|
||||
if err != nil {
|
||||
return fmt.Errorf("an error occurred while creating 4.3.0 client: %w", err)
|
||||
}
|
||||
resourceFhirList, err = client.ProcessBundle(bundle430Data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("an error occurred while processing 4.3.0 resources: %w", err)
|
||||
}
|
||||
case "fhir401":
|
||||
bundle401Data := fhir401.Bundle{}
|
||||
err := base.ParseBundle(bundleFile, &bundle401Data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("an error occurred while parsing 4.0.1 bundle: %w", err)
|
||||
}
|
||||
client, _, err := base.NewFHIR401Client(m.Context, m.AppConfig, m.Logger, *m.Source, http.DefaultClient)
|
||||
if err != nil {
|
||||
return fmt.Errorf("an error occurred while creating 4.0.1 client: %w", err)
|
||||
}
|
||||
resourceFhirList, err = client.ProcessBundle(bundle401Data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("an error occurred while processing 4.0.1 resources: %w", err)
|
||||
}
|
||||
}
|
||||
// we need to upsert all resources (and make sure they are associated with new Source)
|
||||
for _, apiModel := range resourceFhirList {
|
||||
err = db.UpsertResource(context.Background(), &apiModel)
|
||||
if err != nil {
|
||||
return fmt.Errorf("an error occurred while upserting resources: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m ManualClient) ExtractPatientId(bundleFile *os.File) (string, string, error) {
|
||||
// try from newest format to the oldest format
|
||||
bundle430Data := fhir430.Bundle{}
|
||||
bundle401Data := fhir401.Bundle{}
|
||||
|
||||
var patientIds []string
|
||||
|
||||
bundleType := "fhir430"
|
||||
if err := base.ParseBundle(bundleFile, &bundle430Data); err == nil {
|
||||
patientIds = lo.FilterMap[fhir430.BundleEntry, string](bundle430Data.Entry, func(bundleEntry fhir430.BundleEntry, _ int) (string, bool) {
|
||||
parsedResource, err := fhir430utils.MapToResource(bundleEntry.Resource, false)
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
typedResource := parsedResource.(base.ResourceInterface)
|
||||
resourceType, resourceId := typedResource.ResourceRef()
|
||||
|
||||
if resourceId == nil || len(*resourceId) == 0 {
|
||||
return "", false
|
||||
}
|
||||
return *resourceId, resourceType == fhir430.ResourceTypePatient.String()
|
||||
})
|
||||
}
|
||||
bundleFile.Seek(0, io.SeekStart)
|
||||
|
||||
//fallback
|
||||
if patientIds == nil || len(patientIds) == 0 {
|
||||
bundleType = "fhir401"
|
||||
//try parsing the bundle as a 401 bundle
|
||||
//TODO: find a better, more generic way to do this.
|
||||
err := base.ParseBundle(bundleFile, &bundle401Data)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
patientIds = lo.FilterMap[fhir401.BundleEntry, string](bundle401Data.Entry, func(bundleEntry fhir401.BundleEntry, _ int) (string, bool) {
|
||||
parsedResource, err := fhir401utils.MapToResource(bundleEntry.Resource, false)
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
typedResource := parsedResource.(base.ResourceInterface)
|
||||
resourceType, resourceId := typedResource.ResourceRef()
|
||||
|
||||
if resourceId == nil || len(*resourceId) == 0 {
|
||||
return "", false
|
||||
}
|
||||
return *resourceId, resourceType == fhir430.ResourceTypePatient.String()
|
||||
})
|
||||
}
|
||||
bundleFile.Seek(0, io.SeekStart)
|
||||
|
||||
if patientIds == nil || len(patientIds) == 0 {
|
||||
return "", "", fmt.Errorf("could not determine patient id")
|
||||
} else {
|
||||
//reset reader
|
||||
|
||||
return strings.TrimLeft(patientIds[0], "Patient/"), bundleType, nil
|
||||
}
|
||||
}
|
||||
|
||||
func NewClient(ctx context.Context, appConfig config.Interface, globalLogger logrus.FieldLogger, source models.Source, testHttpClient ...*http.Client) (base.Client, *models.Source, error) {
|
||||
return ManualClient{
|
||||
Context: ctx,
|
||||
AppConfig: appConfig,
|
||||
Logger: globalLogger,
|
||||
Source: &models.Source{
|
||||
SourceType: pkg.SourceTypeManual,
|
||||
},
|
||||
}, nil, nil
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ModelBase struct {
|
||||
ID uuid.UUID `json:"id" gorm:"type:uuid;primary_key;"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt *time.Time `json:"deleted_at,omitempty" gorm:"index"`
|
||||
}
|
||||
|
||||
//https://medium.com/@the.hasham.ali/how-to-use-uuid-key-type-with-gorm-cc00d4ec7100
|
||||
|
||||
func (base *ModelBase) BeforeCreate(tx *gorm.DB) error {
|
||||
base.ID = uuid.New()
|
||||
return nil
|
||||
}
|
||||
|
||||
type OriginBase struct {
|
||||
ModelBase
|
||||
User *User `json:"user,omitempty" gorm:"-"`
|
||||
UserID uuid.UUID `json:"user_id"`
|
||||
|
||||
Source *Source `json:"source,omitempty" gorm:"-"`
|
||||
SourceID uuid.UUID `json:"source_id" gorm:"not null;index:,unique,composite:source_resource_id"`
|
||||
|
||||
SourceResourceType string `json:"source_resource_type" gorm:"not null;index:,unique,composite:source_resource_id"`
|
||||
SourceResourceID string `json:"source_resource_id" gorm:"not null;index:,unique,composite:source_resource_id"`
|
||||
}
|
||||
|
||||
func (o OriginBase) GetSourceID() uuid.UUID {
|
||||
return o.SourceID
|
||||
}
|
||||
|
||||
func (o OriginBase) GetSourceResourceType() string {
|
||||
return o.SourceResourceType
|
||||
}
|
||||
func (o OriginBase) GetSourceResourceID() string {
|
||||
return o.SourceResourceID
|
||||
}
|
||||
|
||||
type OriginBaser interface {
|
||||
GetSourceID() uuid.UUID
|
||||
GetSourceResourceType() string
|
||||
GetSourceResourceID() string
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"gorm.io/datatypes"
|
||||
)
|
||||
|
||||
type ResourceFhir struct {
|
||||
OriginBase
|
||||
|
||||
//embedded data
|
||||
Payload datatypes.JSON `json:"payload" gorm:"payload"`
|
||||
}
|
||||
|
||||
type ListResourceQueryOptions struct {
|
||||
SourceID string
|
||||
SourceResourceType string
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// Source Data/Medical Provider Credentials
|
||||
type Source struct {
|
||||
ModelBase
|
||||
User User `json:"user,omitempty"`
|
||||
UserID uuid.UUID `json:"user_id" gorm:"uniqueIndex:idx_user_source_patient"`
|
||||
SourceType pkg.SourceType `json:"source_type" gorm:"uniqueIndex:idx_user_source_patient"`
|
||||
PatientId string `json:"patient_id" gorm:"uniqueIndex:idx_user_source_patient"`
|
||||
|
||||
//oauth endpoints
|
||||
OauthAuthorizationEndpoint string `json:"oauth_authorization_endpoint"`
|
||||
OauthTokenEndpoint string `json:"oauth_token_endpoint"`
|
||||
OauthRegistrationEndpoint string `json:"oauth_registration_endpoint"`
|
||||
OauthIntrospectionEndpoint string `json:"oauth_introspection_endpoint"`
|
||||
OauthUserInfoEndpoint string `json:"oauth_userinfo_endpoint"`
|
||||
OauthTokenEndpointAuthMethods string `json:"oauth_token_endpoint_auth_methods_supported"`
|
||||
|
||||
ApiEndpointBaseUrl string `json:"api_endpoint_base_url"`
|
||||
ClientId string `json:"client_id"`
|
||||
RedirectUri string `json:"redirect_uri"`
|
||||
Scopes string `json:"scopes"`
|
||||
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"`
|
||||
|
||||
Confidential bool `json:"confidential"`
|
||||
}
|
||||
|
||||
/*
|
||||
serverUrl: connectData.message.api_endpoint_base_url,
|
||||
clientId: connectData.message.client_id,
|
||||
redirectUri: connectData.message.redirect_uri,
|
||||
tokenUri: `${connectData.message.oauth_endpoint_base_url}/token`,
|
||||
scope: connectData.message.scopes.join(' '),
|
||||
tokenResponse: payload,
|
||||
expiresAt: getAccessTokenExpiration(payload, new BrowserAdapter()),
|
||||
codeChallenge: codeChallenge,
|
||||
codeVerifier: codeVerifier
|
||||
|
||||
*/
|
|
@ -1,7 +0,0 @@
|
|||
package models
|
||||
|
||||
type SourceSummary struct {
|
||||
Source *Source `json:"source,omitempty"`
|
||||
ResourceTypeCounts []map[string]interface{} `json:"resource_type_counts,omitempty"`
|
||||
Patient *ResourceFhir `json:"patient"`
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
package models
|
||||
|
||||
type Summary struct {
|
||||
Sources []Source `json:"sources,omitempty"`
|
||||
Patients []ResourceFhir `json:"patients,omitempty"`
|
||||
ResourceTypeCounts []map[string]interface{} `json:"resource_type_counts,omitempty"`
|
||||
}
|
|
@ -1,26 +1,6 @@
|
|||
package models
|
||||
|
||||
import "golang.org/x/crypto/bcrypt"
|
||||
|
||||
type User struct {
|
||||
ModelBase
|
||||
Name string `json:"name"`
|
||||
Username string `json:"username" gorm:"unique"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
func (user *User) HashPassword(password string) error {
|
||||
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
user.Password = string(bytes)
|
||||
return nil
|
||||
}
|
||||
func (user *User) CheckPassword(providedPassword string) error {
|
||||
err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(providedPassword))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/auth"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"github.com/gin-gonic/gin"
|
||||
|
@ -12,57 +9,17 @@ import (
|
|||
|
||||
func AuthSignup(c *gin.Context) {
|
||||
databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
|
||||
appConfig := c.MustGet("CONFIG").(config.Interface)
|
||||
|
||||
var user models.User
|
||||
if err := c.ShouldBindJSON(&user); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": err.Error()})
|
||||
return
|
||||
}
|
||||
err := databaseRepo.CreateUser(c, &user)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// return JWT
|
||||
tokenString, err := auth.GenerateJWT(appConfig.GetString("web.jwt.encryptionkey"), user.Username, user.ID.String())
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "data": tokenString})
|
||||
}
|
||||
|
||||
func AuthSignin(c *gin.Context) {
|
||||
databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
|
||||
appConfig := c.MustGet("CONFIG").(config.Interface)
|
||||
|
||||
var user models.User
|
||||
if err := c.ShouldBindJSON(&user); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||
return
|
||||
}
|
||||
|
||||
foundUser, err := databaseRepo.GetUserByEmail(c, user.Username)
|
||||
if err != nil || foundUser == nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": fmt.Sprintf("could not find user: %s", user.Username)})
|
||||
return
|
||||
}
|
||||
|
||||
err = foundUser.CheckPassword(user.Password)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"success": false, "error": fmt.Sprintf("username or password does not match: %s", user.Username)})
|
||||
return
|
||||
}
|
||||
|
||||
// return JWT
|
||||
tokenString, err := auth.GenerateJWT(appConfig.GetString("web.jwt.encryptionkey"), user.Username, user.ID.String())
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "an error occurred generating JWT token"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "data": tokenString})
|
||||
c.JSON(http.StatusOK, gin.H{"success": true})
|
||||
}
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
//"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database"
|
||||
"github.com/gin-gonic/gin"
|
||||
//"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config"
|
||||
//"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func GetHelloWorld(c *gin.Context) {
|
||||
//logger := c.MustGet("LOGGER").(*logrus.Entry)
|
||||
//appConfig := c.MustGet("CONFIG").(config.Interface)
|
||||
|
||||
//device, err := deviceRepo.GetDeviceDetails(c, c.Param("wwn"))
|
||||
//if err != nil {
|
||||
// logger.Errorln("An error occurred while retrieving device details", err)
|
||||
// c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||
// return
|
||||
//}
|
||||
//
|
||||
//durationKey, exists := c.GetQuery("duration_key")
|
||||
//if !exists {
|
||||
// durationKey = "forever"
|
||||
//}
|
||||
//
|
||||
//smartResults, err := deviceRepo.GetSmartAttributeHistory(c, c.Param("wwn"), durationKey, nil)
|
||||
//if err != nil {
|
||||
// logger.Errorln("An error occurred while retrieving device smart results", err)
|
||||
// c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||
// return
|
||||
//}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "data": map[string]interface{}{"hello": "world"}})
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ListResourceFhir(c *gin.Context) {
|
||||
logger := c.MustGet("LOGGER").(*logrus.Entry)
|
||||
databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
|
||||
|
||||
listResourceQueryOptions := models.ListResourceQueryOptions{}
|
||||
if len(c.Query("sourceResourceType")) > 0 {
|
||||
listResourceQueryOptions.SourceResourceType = c.Query("sourceResourceType")
|
||||
}
|
||||
if len(c.Query("sourceID")) > 0 {
|
||||
listResourceQueryOptions.SourceID = c.Query("sourceID")
|
||||
}
|
||||
|
||||
wrappedResourceModels, err := databaseRepo.ListResources(c, listResourceQueryOptions)
|
||||
|
||||
if err != nil {
|
||||
logger.Errorln("An error occurred while retrieving resources", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "data": wrappedResourceModels})
|
||||
}
|
||||
|
||||
//this endpoint retrieves a specific resource by its ID
|
||||
func GetResourceFhir(c *gin.Context) {
|
||||
logger := c.MustGet("LOGGER").(*logrus.Entry)
|
||||
databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
|
||||
|
||||
resourceId := strings.Trim(c.Param("resourceId"), "/")
|
||||
sourceId := strings.Trim(c.Param("sourceId"), "/")
|
||||
wrappedResourceModel, err := databaseRepo.GetResourceBySourceId(c, sourceId, resourceId)
|
||||
|
||||
if err != nil {
|
||||
logger.Errorln("An error occurred while retrieving resource", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "data": wrappedResourceModel})
|
||||
}
|
|
@ -1,202 +0,0 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/hub"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func SourceSync(c *gin.Context) {
|
||||
logger := c.MustGet("LOGGER").(*logrus.Entry)
|
||||
databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
|
||||
|
||||
logger.Infof("Get Source Credentials: %v", c.Param("sourceId"))
|
||||
|
||||
sourceCred, err := databaseRepo.GetSource(c, c.Param("sourceId"))
|
||||
if err != nil {
|
||||
logger.Errorln("An error occurred while retrieving source credential", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||
return
|
||||
}
|
||||
|
||||
// after creating the source, we should do a bulk import
|
||||
err = syncSourceResources(c, logger, databaseRepo, *sourceCred)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "data": sourceCred})
|
||||
}
|
||||
|
||||
func CreateManualSource(c *gin.Context) {
|
||||
logger := c.MustGet("LOGGER").(*logrus.Entry)
|
||||
databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
|
||||
|
||||
// single file
|
||||
file, err := c.FormFile("file")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "could not extract file from form"})
|
||||
return
|
||||
}
|
||||
fmt.Printf("Uploaded filename: %s", file.Filename)
|
||||
|
||||
// create a temporary file to store this uploaded file
|
||||
bundleFile, err := ioutil.TempFile("", file.Filename)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "could not create temp file"})
|
||||
return
|
||||
}
|
||||
|
||||
// Upload the file to specific bundleFile.
|
||||
err = c.SaveUploadedFile(file, bundleFile.Name())
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "could not save temp file"})
|
||||
return
|
||||
}
|
||||
|
||||
// We cannot save the "Source" object yet, as we do not know the patientID
|
||||
|
||||
// create a "manual" client, which we can use to parse the
|
||||
manualSourceClient, _, err := hub.NewClient(pkg.SourceTypeManual, c, nil, logger, models.Source{})
|
||||
if err != nil {
|
||||
logger.Errorln("An error occurred while initializing hub client using manual source without credentials", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||
return
|
||||
}
|
||||
|
||||
err = manualSourceClient.SyncAllBundle(databaseRepo, bundleFile)
|
||||
if err != nil {
|
||||
logger.Errorln("An error occurred while processing bundle", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "data": fmt.Sprintf("'%s' uploaded!", file.Filename)})
|
||||
}
|
||||
|
||||
func GetSource(c *gin.Context) {
|
||||
logger := c.MustGet("LOGGER").(*logrus.Entry)
|
||||
databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
|
||||
|
||||
sourceCred, err := databaseRepo.GetSource(c, c.Param("sourceId"))
|
||||
if err != nil {
|
||||
logger.Errorln("An error occurred while retrieving source credential", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "data": sourceCred})
|
||||
}
|
||||
|
||||
func GetSourceSummary(c *gin.Context) {
|
||||
logger := c.MustGet("LOGGER").(*logrus.Entry)
|
||||
databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
|
||||
|
||||
sourceSummary, err := databaseRepo.GetSourceSummary(c, c.Param("sourceId"))
|
||||
if err != nil {
|
||||
logger.Errorln("An error occurred while retrieving source summary", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "data": sourceSummary})
|
||||
}
|
||||
|
||||
func ListSource(c *gin.Context) {
|
||||
logger := c.MustGet("LOGGER").(*logrus.Entry)
|
||||
databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
|
||||
|
||||
sourceCreds, err := databaseRepo.GetSources(c)
|
||||
if err != nil {
|
||||
logger.Errorln("An error occurred while listing source credentials", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "data": sourceCreds})
|
||||
}
|
||||
|
||||
func RawRequestSource(c *gin.Context) {
|
||||
logger := c.MustGet("LOGGER").(*logrus.Entry)
|
||||
databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
|
||||
|
||||
//!!!!!!INSECURE!!!!!!S
|
||||
//We're setting the username to a user provided value, this is insecure, but required for calling databaseRepo fns
|
||||
c.Set("AUTH_USERNAME", c.Param("username"))
|
||||
|
||||
foundSource, err := databaseRepo.GetSource(c, c.Param("sourceId"))
|
||||
if err != nil {
|
||||
logger.Errorf("An error occurred while finding source credential: %v", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if foundSource == nil {
|
||||
logger.Errorf("Did not source credentials for %s", c.Param("sourceType"))
|
||||
c.JSON(http.StatusNotFound, gin.H{"success": false, "error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
client, updatedSource, err := hub.NewClient(foundSource.SourceType, c, nil, logger, *foundSource)
|
||||
if err != nil {
|
||||
logger.Errorf("Could not initialize source client %v", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": err.Error()})
|
||||
return
|
||||
}
|
||||
if updatedSource != nil {
|
||||
err := databaseRepo.CreateSource(c, updatedSource)
|
||||
if err != nil {
|
||||
logger.Errorf("An error occurred while updating source credential %v", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": err.Error()})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var resp map[string]interface{}
|
||||
|
||||
parsedUrl, err := url.Parse(strings.TrimSuffix(c.Param("path"), "/"))
|
||||
if err != nil {
|
||||
logger.Errorf("Error parsing request, %v", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": err.Error()})
|
||||
return
|
||||
}
|
||||
//make sure we include all query string parameters with the raw request.
|
||||
parsedUrl.RawQuery = c.Request.URL.Query().Encode()
|
||||
|
||||
err = client.GetRequest(parsedUrl.String(), &resp)
|
||||
if err != nil {
|
||||
logger.Errorf("Error making raw request, %v", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": err.Error(), "data": resp})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "data": resp})
|
||||
}
|
||||
|
||||
////// private functions
|
||||
func syncSourceResources(c *gin.Context, logger *logrus.Entry, databaseRepo database.DatabaseRepository, sourceCred models.Source) error {
|
||||
// after creating the source, we should do a bulk import
|
||||
sourceClient, updatedSource, err := hub.NewClient(sourceCred.SourceType, c, nil, logger, sourceCred)
|
||||
if err != nil {
|
||||
logger.Errorln("An error occurred while initializing hub client using source credential", err)
|
||||
return err
|
||||
}
|
||||
if updatedSource != nil {
|
||||
err := databaseRepo.CreateSource(c, updatedSource)
|
||||
if err != nil {
|
||||
logger.Errorln("An error occurred while updating source credential", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = sourceClient.SyncAll(databaseRepo)
|
||||
if err != nil {
|
||||
logger.Errorln("An error occurred while bulk import of resources from source", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func GetSummary(c *gin.Context) {
|
||||
logger := c.MustGet("LOGGER").(*logrus.Entry)
|
||||
databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
|
||||
|
||||
summary, err := databaseRepo.GetSummary(c)
|
||||
if err != nil {
|
||||
logger.Errorln("An error occurred while retrieving summary", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "data": summary})
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/auth"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config"
|
||||
"github.com/gin-gonic/gin"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func RequireAuth() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
appConfig := c.MustGet("CONFIG").(config.Interface)
|
||||
|
||||
authHeader := c.GetHeader("Authorization")
|
||||
authHeaderParts := strings.Split(authHeader, " ")
|
||||
|
||||
if len(authHeaderParts) != 2 {
|
||||
log.Println("Authentication header is invalid: " + authHeader)
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"success": false, "error": "request does not contain a valid token"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
tokenString := authHeaderParts[1]
|
||||
|
||||
if tokenString == "" {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"success": false, "error": "request does not contain an access token"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
claim, err := auth.ValidateToken(appConfig.GetString("web.jwt.encryptionkey"), tokenString)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"success": false, "error": err.Error()})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
//todo, is this shared between all sessions??
|
||||
c.Set("AUTH_TOKEN", tokenString)
|
||||
c.Set("AUTH_USERNAME", claim.Username)
|
||||
c.Set("AUTH_USERID", claim.UserId)
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
|
@ -41,30 +41,7 @@ func (ae *AppEngine) Setup(logger *logrus.Entry) *gin.Engine {
|
|||
})
|
||||
|
||||
api.POST("/auth/signup", handler.AuthSignup)
|
||||
api.POST("/auth/signin", handler.AuthSignin)
|
||||
|
||||
secure := api.Group("/secure").Use(middleware.RequireAuth())
|
||||
{
|
||||
secure.GET("/summary", handler.GetSummary)
|
||||
|
||||
secure.POST("/source/manual", handler.CreateManualSource)
|
||||
secure.GET("/source", handler.ListSource)
|
||||
secure.GET("/source/:sourceId", handler.GetSource)
|
||||
secure.POST("/source/:sourceId/sync", handler.SourceSync)
|
||||
secure.GET("/source/:sourceId/summary", handler.GetSourceSummary)
|
||||
secure.GET("/resource/fhir", handler.ListResourceFhir) //
|
||||
secure.GET("/resource/fhir/:sourceId/:resourceId", handler.GetResourceFhir)
|
||||
}
|
||||
|
||||
api.GET("/metadata/source", handler.GetMetadataSource)
|
||||
|
||||
if ae.Config.GetString("log.level") == "DEBUG" {
|
||||
//in debug mode, this endpoint lets us request data directly from the source api
|
||||
ae.Logger.Warningf("***INSECURE*** ***INSECURE*** DEBUG mode enables developer functionality, including unauthenticated raw api requests")
|
||||
|
||||
//http://localhost:9090/api/raw/test@test.com/436d7277-ad56-41ce-9823-44e353d1b3f6/Patient/smart-1288992
|
||||
api.GET("/raw/:username/:sourceId/*path", handler.RawRequestSource)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
version: "3.9"
|
||||
services:
|
||||
couchdb:
|
||||
build:
|
||||
context: ./docker/couchdb
|
||||
dockerfile: Dockerfile
|
||||
# environment:
|
||||
# - COUCHDB_USER=admin
|
||||
# - COUCHDB_PASSWORD=password
|
||||
ports:
|
||||
- "5984:5984"
|
||||
volumes:
|
||||
- ./.couchdb/data:/opt/couchdb/data
|
||||
- ./.couchdb/config:/opt/couchdb/etc/local.d
|
|
@ -0,0 +1,3 @@
|
|||
FROM couchdb:3.2
|
||||
|
||||
COPY local.ini /opt/couchdb/etc/
|
|
@ -0,0 +1,108 @@
|
|||
; CouchDB Configuration Settings
|
||||
|
||||
; Custom settings should be made in this file. They will override settings
|
||||
; in default.ini, but unlike changes made to default.ini, this file won't be
|
||||
; overwritten on server upgrade.
|
||||
|
||||
[cors]
|
||||
origins = *
|
||||
headers = accept, authorization, content-type, origin, referer
|
||||
credentials = true
|
||||
methods = GET, PUT, POST, HEAD, DELETE
|
||||
|
||||
[couchdb]
|
||||
;max_document_size = 4294967296 ; bytes
|
||||
;os_process_timeout = 5000
|
||||
single_node=true
|
||||
|
||||
[couch_peruser]
|
||||
; If enabled, couch_peruser ensures that a private per-user database
|
||||
; exists for each document in _users. These databases are writable only
|
||||
; by the corresponding user. Databases are in the following form:
|
||||
; userdb-{hex encoded username}
|
||||
enable = true
|
||||
; If set to true and a user is deleted, the respective database gets
|
||||
; deleted as well.
|
||||
;delete_dbs = true
|
||||
; Set a default q value for peruser-created databases that is different from
|
||||
; cluster / q
|
||||
;q = 1
|
||||
|
||||
[chttpd]
|
||||
;port = 5984
|
||||
;bind_address = 127.0.0.1
|
||||
; Options for the MochiWeb HTTP server.
|
||||
;server_options = [{backlog, 128}, {acceptor_pool_size, 16}]
|
||||
; For more socket options, consult Erlang's module 'inet' man page.
|
||||
;socket_options = [{sndbuf, 262144}, {nodelay, true}]
|
||||
bind_address = 0.0.0.0
|
||||
enable_cors = true
|
||||
x_forwarded_host = X-Forwarded-Host
|
||||
|
||||
[httpd]
|
||||
; NOTE that this only configures the "backend" node-local port, not the
|
||||
; "frontend" clustered port. You probably don't want to change anything in
|
||||
; this section.
|
||||
; Uncomment next line to trigger basic-auth popup on unauthorized requests.
|
||||
;WWW-Authenticate = Basic realm="administrator"
|
||||
|
||||
; Uncomment next line to set the configuration modification whitelist. Only
|
||||
; whitelisted values may be changed via the /_config URLs. To allow the admin
|
||||
; to change this value over HTTP, remember to include {httpd,config_whitelist}
|
||||
; itself. Excluding it from the list would require editing this file to update
|
||||
; the whitelist.
|
||||
;config_whitelist = [{httpd,config_whitelist}, {log,level}, {etc,etc}]
|
||||
enable_cors = true
|
||||
|
||||
[chttpd_auth]
|
||||
; If you set this to true, you should also uncomment the WWW-Authenticate line
|
||||
; above. If you don't configure a WWW-Authenticate header, CouchDB will send
|
||||
; Basic realm="server" in order to prevent you getting logged out.
|
||||
; require_valid_user = false
|
||||
allow_persistent_cookies = true
|
||||
;cookie_domain = localhost:5984
|
||||
|
||||
[ssl]
|
||||
;enable = true
|
||||
;cert_file = /full/path/to/server_cert.pem
|
||||
;key_file = /full/path/to/server_key.pem
|
||||
;password = somepassword
|
||||
; set to true to validate peer certificates
|
||||
;verify_ssl_certificates = false
|
||||
; Set to true to fail if the client does not send a certificate. Only used if verify_ssl_certificates is true.
|
||||
;fail_if_no_peer_cert = false
|
||||
; Path to file containing PEM encoded CA certificates (trusted
|
||||
; certificates used for verifying a peer certificate). May be omitted if
|
||||
; you do not want to verify the peer.
|
||||
;cacert_file = /full/path/to/cacertf
|
||||
; The verification fun (optional) if not specified, the default
|
||||
; verification fun will be used.
|
||||
;verify_fun = {Module, VerifyFun}
|
||||
; maximum peer certificate depth
|
||||
;ssl_certificate_max_depth = 1
|
||||
;
|
||||
; Reject renegotiations that do not live up to RFC 5746.
|
||||
;secure_renegotiate = true
|
||||
; The cipher suites that should be supported.
|
||||
; Can be specified in erlang format "{ecdhe_ecdsa,aes_128_cbc,sha256}"
|
||||
; or in OpenSSL format "ECDHE-ECDSA-AES128-SHA256".
|
||||
;ciphers = ["ECDHE-ECDSA-AES128-SHA256", "ECDHE-ECDSA-AES128-SHA"]
|
||||
; The SSL/TLS versions to support
|
||||
;tls_versions = [tlsv1, 'tlsv1.1', 'tlsv1.2']
|
||||
|
||||
; To enable Virtual Hosts in CouchDB, add a vhost = path directive. All requests to
|
||||
; the Virual Host will be redirected to the path. In the example below all requests
|
||||
; to http://example.com/ are redirected to /database.
|
||||
; If you run CouchDB on a specific port, include the port number in the vhost:
|
||||
; example.com:5984 = /database
|
||||
[vhosts]
|
||||
;example.com = /database/
|
||||
|
||||
; To create an admin account uncomment the '[admins]' section below and add a
|
||||
; line in the format 'username = password'. When you next start CouchDB, it
|
||||
; will change the password to a hash (so that your passwords don't linger
|
||||
; around in plain-text files). You can add more admin accounts with more
|
||||
; 'username = password' lines. Don't forget to restart CouchDB after
|
||||
; changing this.
|
||||
[admins]
|
||||
admin = mysecretpassword
|
34
go.mod
34
go.mod
|
@ -4,40 +4,25 @@ go 1.18
|
|||
|
||||
require (
|
||||
github.com/analogj/go-util v0.0.0-20210417161720-39b497cca03b
|
||||
github.com/fastenhealth/gofhir-models v0.0.4
|
||||
github.com/gin-gonic/gin v1.8.1
|
||||
github.com/glebarez/sqlite v1.4.6
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||
github.com/go-kivik/couchdb/v3 v3.3.0
|
||||
github.com/go-kivik/kivik/v3 v3.2.3
|
||||
github.com/golang/mock v1.4.4
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/samber/lo v1.27.1
|
||||
github.com/seborama/govcr v4.5.0+incompatible
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
github.com/spf13/viper v1.12.0
|
||||
github.com/stretchr/testify v1.7.1
|
||||
github.com/urfave/cli/v2 v2.11.2
|
||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4
|
||||
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5
|
||||
gorm.io/datatypes v1.0.7
|
||||
gorm.io/gorm v1.23.8
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/fatih/color v1.13.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/glebarez/go-sqlite v1.17.3 // indirect
|
||||
github.com/go-playground/locales v0.14.0 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.10.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
||||
github.com/goccy/go-json v0.9.7 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kvz/logstreamer v0.0.0-20150507115422-a635b98146f0 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
|
@ -50,8 +35,6 @@ require (
|
|||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/spf13/afero v1.8.2 // indirect
|
||||
github.com/spf13/cast v1.5.0 // indirect
|
||||
|
@ -60,19 +43,14 @@ require (
|
|||
github.com/subosito/gotenv v1.3.0 // indirect
|
||||
github.com/ugorji/go/codec v1.2.7 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect
|
||||
golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 // indirect
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect
|
||||
golang.org/x/net v0.0.0-20221004154528-8021a29435af // indirect
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
gopkg.in/ini.v1 v1.66.4 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gorm.io/driver/mysql v1.3.2 // indirect
|
||||
modernc.org/libc v1.16.8 // indirect
|
||||
modernc.org/mathutil v1.4.1 // indirect
|
||||
modernc.org/memory v1.1.1 // indirect
|
||||
modernc.org/sqlite v1.17.3 // indirect
|
||||
)
|
||||
|
|
249
go.sum
249
go.sum
|
@ -1,3 +1,4 @@
|
|||
bou.ke/monkey v1.0.1/go.mod h1:FgHuK96Rv2Nlf+0u1OOVDpCMdsWyOFmeeketDHE7LIg=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
|
@ -36,11 +37,9 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
|
|||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
|
||||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
github.com/analogj/go-util v0.0.0-20210417161720-39b497cca03b h1:Y/+MfmdKPPpVY7C6ggt/FpltFSitlpUtyJEdcQyFXQg=
|
||||
github.com/analogj/go-util v0.0.0-20210417161720-39b497cca03b/go.mod h1:bRSzJXgXnT5+Ihah7RSC7Cvp16UmoLn3wq6ROciS1Ow=
|
||||
|
@ -52,31 +51,23 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
|
|||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denisenkom/go-mssqldb v0.12.0 h1:VtrkII767ttSPNRfFekePK3sctr+joXgO58stqQbtUA=
|
||||
github.com/denisenkom/go-mssqldb v0.12.0/go.mod h1:iiK0YP1ZeepvmBQk/QpLEhhTNJgfzrpArPY/aFvc9yU=
|
||||
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fastenhealth/gofhir-models v0.0.4 h1:Q//StwNXGfK+WAS2DckGBHAP1R4cHMRZEF/sLGgmR04=
|
||||
github.com/fastenhealth/gofhir-models v0.0.4/go.mod h1:xB8ikGxu3bUq2b1JYV+CZpHqBaLXpOizFR0eFBCunis=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/flimzy/diff v0.1.5/go.mod h1:lFJtC7SPsK0EroDmGTSrdtWKAxOk3rO+q+e04LL05Hs=
|
||||
github.com/flimzy/testy v0.1.17-0.20190521133342-95b386c3ece6/go.mod h1:3szguN8NXqgq9bt9Gu8TQVj698PJWmyx/VY1frwwKrM=
|
||||
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
|
||||
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
|
||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||
|
@ -84,15 +75,17 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE
|
|||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8=
|
||||
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
|
||||
github.com/glebarez/go-sqlite v1.17.3 h1:Rji9ROVSTTfjuWD6j5B+8DtkNvPILoUC3xRhkQzGxvk=
|
||||
github.com/glebarez/go-sqlite v1.17.3/go.mod h1:Hg+PQuhUy98XCxWEJEaWob8x7lhJzhNYF1nZbUiRGIY=
|
||||
github.com/glebarez/sqlite v1.4.6 h1:D5uxD2f6UJ82cHnVtO2TZ9pqsLyto3fpDKHIk2OsR8A=
|
||||
github.com/glebarez/sqlite v1.4.6/go.mod h1:WYEtEFjhADPaPJqL/PGlbQQGINBA3eUAfDNbKFJf/zA=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-kivik/couchdb/v3 v3.3.0 h1:xipDIw66Sqei550OzSXWlLmJ9y01fMbBLkX0F/snjnw=
|
||||
github.com/go-kivik/couchdb/v3 v3.3.0/go.mod h1:idTUN/G5HGE+YP2EgW+dvZ5ODkdhvddcU7ZzFZfwN6Q=
|
||||
github.com/go-kivik/kivik/v3 v3.0.1/go.mod h1:7tmQDvkta/pcijpUjLMsQ9HJUELiKD5zm6jQ3Gb9cxE=
|
||||
github.com/go-kivik/kivik/v3 v3.2.0/go.mod h1:chqVuHKAU9j2C7qL0cAH2FCO26oL+0B4aIBeCRMnLa8=
|
||||
github.com/go-kivik/kivik/v3 v3.2.3 h1:ZFGR3hMDa+AUmPUCQxq4da3+3C4awdFQwdOtjLS+MxM=
|
||||
github.com/go-kivik/kivik/v3 v3.2.3/go.mod h1:chqVuHKAU9j2C7qL0cAH2FCO26oL+0B4aIBeCRMnLa8=
|
||||
github.com/go-kivik/kiviktest/v3 v3.0.4 h1:mHX/9gpz5VdSOOOs7sN0/6iLK6jQcLiYbteEd33cKNo=
|
||||
github.com/go-kivik/kiviktest/v3 v3.0.4/go.mod h1:sqsz3M2sJxTxAUdOj+2SU21y4phcpYc0FJIn+hbf1D0=
|
||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
|
||||
|
@ -101,19 +94,8 @@ github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/j
|
|||
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||
github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0=
|
||||
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM=
|
||||
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 h1:+eHOFJl1BaXrQxKX+T06f78590z4qA2ZzBTqahsKSE4=
|
||||
github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188/go.mod h1:vXjM/+wXQnTPR4KqTKDgJukSZ6amVRtWMPEjE6sQoK8=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
|
@ -141,8 +123,6 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
|
|||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
|
@ -153,7 +133,6 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
|
@ -173,85 +152,29 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe
|
|||
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 h1:l5lAOZEym3oK3SQ2HBHWsJUfbNBiTXJDeW2QDxw9AQ0=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
|
||||
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
||||
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
|
||||
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
|
||||
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
|
||||
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
|
||||
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
|
||||
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
|
||||
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
|
||||
github.com/jackc/pgconn v1.11.0 h1:HiHArx4yFbwl91X3qqIHtUFoiIfLNJXCQRsnzkiwwaQ=
|
||||
github.com/jackc/pgconn v1.11.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
|
||||
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
|
||||
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
|
||||
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
|
||||
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
|
||||
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
|
||||
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.2.0 h1:r7JypeP2D3onoQTCxWdTpCtJ4D+qpKr0TxvoyMhZ5ns=
|
||||
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/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=
|
||||
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
|
||||
github.com/jackc/pgtype v1.10.0 h1:ILnBWrRMSXGczYvmkYD6PsYyVFUNLTnIUJHHDLmqk38=
|
||||
github.com/jackc/pgtype v1.10.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
|
||||
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
|
||||
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
|
||||
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
||||
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/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=
|
||||
github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
|
@ -259,27 +182,16 @@ github.com/kvz/logstreamer v0.0.0-20150507115422-a635b98146f0 h1:3tLzEnUizyN9YLW
|
|||
github.com/kvz/logstreamer v0.0.0-20150507115422-a635b98146f0/go.mod h1:8/LTPeDLaklcUjgSQBHbhBF1ibKAFxzS5o+H7USfMSA=
|
||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
|
||||
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.6/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.12 h1:TJ1bhYJPV44phC+IMu1u2K/i5RriLTPe+yc68XDJ1Z0=
|
||||
github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
|
@ -289,39 +201,33 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
|
|||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
|
||||
github.com/otiai10/copy v1.0.2/go.mod h1:c7RpqBkwMom4bYTSkLSym4VSJz/XtncWRAj/J4PEIMY=
|
||||
github.com/otiai10/copy v1.7.0 h1:hVoPiN+t+7d2nzzwMiDHPSOogsWAStewq3TwU05+clE=
|
||||
github.com/otiai10/copy v1.7.0/go.mod h1:rmRl6QPdJj6EiUqXQ/4Nn2lLXoNQjFCQbbNrxgc/t3U=
|
||||
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
|
||||
github.com/otiai10/curr v0.0.0-20190513014714-f5a3d24e5776/go.mod h1:3HNVkVOU7vZeFXocWuvtcS0XSFLcf2XUSDHkq9t1jU4=
|
||||
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
|
||||
github.com/otiai10/mint v1.2.4/go.mod h1:d+b7n/0R3tdyUYYylALXpWQ/kTN+QobSq/4SRGBkR3M=
|
||||
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
|
||||
github.com/otiai10/mint v1.3.3/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
|
||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU=
|
||||
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
|
||||
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/samber/lo v1.27.1 h1:sTXwkRiIFIQG+G0HeAvOEnGjqWeWtI9cg5/n51KrxPg=
|
||||
github.com/samber/lo v1.27.1/go.mod h1:it33p9UtPMS7z72fP4gw/EIfQB2eI8ke7GR2wc6+Rhg=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/seborama/govcr v4.5.0+incompatible h1:XvdHtXi0d4cUAn+0aWolvwfS3nmhNC8Z+yMQwn/M64I=
|
||||
github.com/seborama/govcr v4.5.0+incompatible/go.mod h1:EgcISudCCYDLzbiAImJ8i7kk4+wTA44Kp+j4S0LhASI=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo=
|
||||
|
@ -335,8 +241,6 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
|
|||
github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ=
|
||||
github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
|
@ -347,7 +251,6 @@ github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMT
|
|||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI=
|
||||
github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs=
|
||||
github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M=
|
||||
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
||||
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||
|
@ -359,40 +262,24 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
|||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||
gitlab.com/flimzy/testy v0.0.3/go.mod h1:YObF4cq711ubd/3U0ydRQQVz7Cnq/ChgJpVwNr/AJac=
|
||||
gitlab.com/flimzy/testy v0.9.1 h1:c255KNz+9lqkMAmC4TYngLP1mOkBJ30KziNgz9r7apk=
|
||||
gitlab.com/flimzy/testy v0.9.1/go.mod h1:4Yy2aelUY6IgRmd3jHgGWewZLNMQZWQTlwyuMW642Mc=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA=
|
||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
|
@ -405,8 +292,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
|||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM=
|
||||
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
|
@ -442,7 +327,6 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR
|
|||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
|
@ -459,14 +343,12 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
|
|||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 h1:NWy5+hlRbC7HK+PmcXVUmW1IMyFce7to56IUvhUFm7Y=
|
||||
golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20221004154528-8021a29435af h1:wv66FM3rLZGPdxpYL+ApnDe2HzHcTFta3z5nsc13wI4=
|
||||
golang.org/x/net v0.0.0-20221004154528-8021a29435af/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
|
@ -476,8 +358,6 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ
|
|||
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 h1:OSnWWcOd/CtWQC2cYSBgbTSJv3ciqd8r54ySIW2y3RE=
|
||||
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@ -489,22 +369,17 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -528,19 +403,15 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
|
@ -563,18 +434,14 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
|
|||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
|
@ -582,7 +449,6 @@ golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtn
|
|||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
|
@ -603,18 +469,17 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc
|
|||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
|
@ -640,7 +505,6 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
|
|||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
|
@ -705,7 +569,6 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
|
|||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
@ -713,31 +576,15 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
|
|||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||
gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4=
|
||||
gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/datatypes v1.0.7 h1:8NhJN4+annFjwV1WufDhFiPjdUvV1lSGUdg1UCjQIWY=
|
||||
gorm.io/datatypes v1.0.7/go.mod h1:l9qkCuy0CdzDEop9HKUdcnC9gHC2sRlaFtHkTzsZRqg=
|
||||
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/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=
|
||||
gorm.io/driver/sqlserver v1.3.1/go.mod h1:w25Vrx2BG+CJNUu/xKbFhaKlGxT/nzRkhWCCoptX8tQ=
|
||||
gorm.io/gorm v1.23.1/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||
gorm.io/gorm v1.23.6/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||
gorm.io/gorm v1.23.8 h1:h8sGJ+biDgBA1AD1Ha9gFCx7h8npU7AsLdlkX0n2TpE=
|
||||
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||
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=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
@ -745,32 +592,6 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
|
|||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||
modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
|
||||
modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc=
|
||||
modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw=
|
||||
modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ=
|
||||
modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ=
|
||||
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
|
||||
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
|
||||
modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA=
|
||||
modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A=
|
||||
modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU=
|
||||
modernc.org/libc v1.16.7/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU=
|
||||
modernc.org/libc v1.16.8 h1:Ux98PaOMvolgoFX/YwusFOHBnanXdGRmWgI8ciI2z4o=
|
||||
modernc.org/libc v1.16.8/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU=
|
||||
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8=
|
||||
modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/memory v1.1.1 h1:bDOL0DIDLQv7bWhP3gMvIrnoFw+Eo6F7a2QK9HPDiFU=
|
||||
modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
|
||||
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/sqlite v1.17.3 h1:iE+coC5g17LtByDYDWKpR6m2Z9022YrSh3bumwOnIrI=
|
||||
modernc.org/sqlite v1.17.3/go.mod h1:10hPVYar9C0kfXuTWGz8s0XtB8uAGymUy51ZzStYe3k=
|
||||
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
|
||||
modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw=
|
||||
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
|
|
Loading…
Reference in New Issue