You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
373 lines
9.3 KiB
373 lines
9.3 KiB
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"regexp"
|
|
|
|
"google.golang.org/grpc"
|
|
"gorm.io/gorm"
|
|
"whiteboxsystems.nl/openkvpoc/his/model"
|
|
"whiteboxsystems.nl/openkvpoc/openkv"
|
|
"whiteboxsystems.nl/openkvpoc/sharedmodel"
|
|
)
|
|
|
|
const CONN_PSK = "0000"
|
|
|
|
func getUnauthenticatedClient(addr string) (openkv.OpenKVClient, error) {
|
|
opts := []grpc.DialOption{
|
|
grpc.WithInsecure(), // dont do this in any production env...
|
|
}
|
|
|
|
conn, err := grpc.Dial(addr, opts...)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// defer conn.Close()
|
|
return openkv.NewOpenKVClient(conn), nil
|
|
}
|
|
|
|
func getAuthenticatedClient(addr, psk string) (openkv.OpenKVClient, error) {
|
|
opts := []grpc.DialOption{
|
|
grpc.WithPerRPCCredentials(makePSKAuth(psk, true)),
|
|
grpc.WithInsecure(), // dont do this in any production env...
|
|
}
|
|
conn, err := grpc.Dial(addr, opts...)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// defer conn.Close()
|
|
return openkv.NewOpenKVClient(conn), nil
|
|
}
|
|
|
|
type PSKAuth struct {
|
|
psk string
|
|
insecure bool
|
|
}
|
|
|
|
func (ma PSKAuth) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
|
|
return map[string]string{
|
|
"authorization": ma.psk,
|
|
}, nil
|
|
}
|
|
|
|
func (ma PSKAuth) RequireTransportSecurity() bool {
|
|
return !ma.insecure
|
|
}
|
|
|
|
func makePSKAuth(psk string, insecure bool) *PSKAuth {
|
|
return &PSKAuth{psk, insecure}
|
|
}
|
|
|
|
func (srv *HISServer) register(addr string) (*model.Connection, error) {
|
|
client, err := getUnauthenticatedClient(addr)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
auth := &openkv.AuthConfig{
|
|
Method: openkv.AuthMethod_APIToken,
|
|
Config: &openkv.AuthConfig_ApiTokenConfig{&openkv.APITokenConfig{Token: CONN_PSK}},
|
|
}
|
|
|
|
resp, err := client.Register(context.Background(), &openkv.RegisterRequest{
|
|
OrganisationId: "00009999",
|
|
OrganisationIdSystem: "https://vektis.nl/agbz",
|
|
OrganisationDisplayName: "Praktijk de oude berg",
|
|
Auth: auth,
|
|
})
|
|
|
|
if err != nil {
|
|
log.Printf("Err in request: %v", err)
|
|
return nil, err
|
|
}
|
|
|
|
if !resp.Success {
|
|
return nil, fmt.Errorf("%v", resp.Error.Message)
|
|
}
|
|
|
|
connection := &model.Connection{
|
|
Addr: addr,
|
|
AuthConfig: &sharedmodel.AuthConfig{
|
|
Method: openkv.AuthMethod_APIToken,
|
|
Raw: CONN_PSK,
|
|
},
|
|
State: model.ConnectionStatePending,
|
|
Reference: resp.Reference,
|
|
}
|
|
|
|
meta, _ := srv.listMeta(connection)
|
|
|
|
connection.Supplier = meta.Supplier
|
|
connection.System = meta.System
|
|
|
|
if err := srv.data.Create(connection).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return connection, nil
|
|
}
|
|
|
|
func (srv *HISServer) activate(conn *model.Connection, psk string) (*model.Connection, error) {
|
|
client, err := getAuthenticatedClient(conn.Addr, conn.AuthConfig.Raw)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
resp, err := client.CompleteRegistration(context.Background(), &openkv.CompleteRegistrationRequest{
|
|
Reference: conn.Reference,
|
|
RegistrationToken: psk,
|
|
})
|
|
|
|
if err != nil {
|
|
log.Printf("Err in request: %v", err)
|
|
return nil, err
|
|
} else if !resp.Success {
|
|
log.Printf("success: %v; Err: %v", resp.Success, resp.Error)
|
|
return nil, fmt.Errorf("%v", resp.Error.Message)
|
|
}
|
|
|
|
meta, err := srv.listMeta(conn)
|
|
|
|
if err != nil {
|
|
return conn, fmt.Errorf("Failed to retreive metadata: %v ", err)
|
|
}
|
|
|
|
conn.State = model.ConnectionStateCompleted
|
|
conn.Supplier = meta.Supplier
|
|
conn.System = meta.System
|
|
|
|
if err := srv.data.Save(conn).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return conn, err
|
|
|
|
}
|
|
|
|
func (srv *HISServer) listMeta(conn *model.Connection) (*openkv.GetMetadataResponse, error) {
|
|
client, err := getUnauthenticatedClient(conn.Addr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
resp, err := client.GetMetadata(context.Background(), &openkv.GetMetadataRequest{})
|
|
|
|
if err != nil {
|
|
log.Printf("Err in request: %v", err)
|
|
return nil, err
|
|
} else if !resp.Success {
|
|
log.Printf("success: %v; Err: %v", resp.Success, resp.Error)
|
|
return nil, fmt.Errorf("%v", resp.Error.Message)
|
|
}
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
func (srv *HISServer) enableService(conn *model.Connection, service string, active bool) error {
|
|
client, err := getAuthenticatedClient(conn.Addr, conn.AuthConfig.Raw)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
moddedService := &model.Service{}
|
|
moddedServiceErr := srv.data.Where("connection_id = ? and service_id = ?", conn.ID, service).First(moddedService).Error
|
|
|
|
if moddedServiceErr != nil && moddedServiceErr != gorm.ErrRecordNotFound {
|
|
return moddedServiceErr
|
|
}
|
|
|
|
if moddedServiceErr != nil && !active {
|
|
return nil
|
|
}
|
|
|
|
if moddedServiceErr == nil && active {
|
|
return nil
|
|
}
|
|
|
|
meta, _ := srv.listMeta(conn)
|
|
var serviceDefinition *openkv.ServiceDefinition
|
|
|
|
for _, sd := range meta.Services {
|
|
if sd.Id == service {
|
|
serviceDefinition = sd
|
|
}
|
|
}
|
|
|
|
if serviceDefinition == nil {
|
|
return fmt.Errorf("Invalid service: %v", service)
|
|
}
|
|
|
|
var resp *openkv.ConfigServiceResponse
|
|
|
|
if m, _ := regexp.MatchString("wbx:*", service); m { // Whitebox
|
|
resp, err = client.ConfigService(context.Background(), &openkv.ConfigServiceRequest{
|
|
Service: service,
|
|
Enabled: active,
|
|
Fetch: &openkv.ServiceConfig{
|
|
Protocol: "https://whiteboxsystems.nl/protospecs/whitebox-fetch/http",
|
|
Config: map[string]string{
|
|
"url": externalAddr + "/external/api",
|
|
},
|
|
Auth: &openkv.AuthConfig{
|
|
Method: openkv.AuthMethod_APIToken,
|
|
},
|
|
},
|
|
Push: &openkv.ServiceConfig{
|
|
Protocol: "https://whiteboxsystems.nl/protospecs/whitebox-push/http",
|
|
Config: map[string]string{
|
|
"url": externalAddr + "/external/api",
|
|
},
|
|
Auth: &openkv.AuthConfig{
|
|
Method: openkv.AuthMethod_APIToken,
|
|
},
|
|
},
|
|
})
|
|
} else if m, _ := regexp.MatchString("voorbeeld:*", service); m { // Kis
|
|
resp, err = client.ConfigService(context.Background(), &openkv.ConfigServiceRequest{
|
|
Service: service,
|
|
Enabled: active,
|
|
Fetch: &openkv.ServiceConfig{
|
|
Protocol: "https://whiteboxsystems.nl/protospecs/whitebox-fetch/http",
|
|
Config: map[string]string{
|
|
"url": externalAddr + "/external/api",
|
|
},
|
|
Auth: &openkv.AuthConfig{
|
|
Method: openkv.AuthMethod_APIToken,
|
|
},
|
|
},
|
|
Push: &openkv.ServiceConfig{
|
|
Protocol: "https://whiteboxsystems.nl/protospecs/whitebox-push/http",
|
|
Config: map[string]string{
|
|
"url": externalAddr + "/external/api",
|
|
},
|
|
Auth: &openkv.AuthConfig{
|
|
Method: openkv.AuthMethod_APIToken,
|
|
},
|
|
},
|
|
})
|
|
} else { // DVZA / FHIR
|
|
resp, err = client.ConfigService(context.Background(), &openkv.ConfigServiceRequest{
|
|
Service: service,
|
|
Enabled: active,
|
|
Fetch: &openkv.ServiceConfig{
|
|
Protocol: "https://hl7.org/fhir",
|
|
Config: map[string]string{
|
|
"url": externalAddr + "/external/fhir/Patient",
|
|
},
|
|
Auth: &openkv.AuthConfig{
|
|
Method: openkv.AuthMethod_APIToken,
|
|
},
|
|
},
|
|
Push: &openkv.ServiceConfig{
|
|
Protocol: "https://hl7.org/fhir",
|
|
Config: map[string]string{
|
|
"url": externalAddr + "/external/fhir/Patient",
|
|
},
|
|
Auth: &openkv.AuthConfig{
|
|
Method: openkv.AuthMethod_APIToken,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
if err != nil {
|
|
log.Printf("Err in request: %v", err)
|
|
return err
|
|
} else if !resp.Success {
|
|
log.Printf("success: %v; Err: %v", resp.Success, resp.Error)
|
|
return fmt.Errorf("%v", resp.Error)
|
|
}
|
|
|
|
if !active {
|
|
subs := []model.Patient{}
|
|
|
|
srv.data.Model(moddedService).Association("Subscriptions").Find(&subs)
|
|
srv.data.Model(moddedService).Association("Subscriptions").Delete(subs)
|
|
srv.subscribePatients(conn, moddedService, false, subs)
|
|
return srv.data.Unscoped().Delete(moddedService).Error
|
|
}
|
|
|
|
return srv.data.Create(&model.Service{
|
|
ConnectionID: conn.ID,
|
|
ServiceID: serviceDefinition.Id,
|
|
Name: serviceDefinition.Name,
|
|
Description: serviceDefinition.Name,
|
|
SubscriptionPolicy: serviceDefinition.SubscriptionPolicy,
|
|
ConsentPolicy: serviceDefinition.ConsentPolicy,
|
|
AuthConfig: &sharedmodel.AuthConfig{
|
|
Method: resp.Fetch.Auth.Method,
|
|
Raw: resp.Fetch.Auth.GetApiTokenConfig().Token,
|
|
},
|
|
}).Error
|
|
}
|
|
|
|
func (srv *HISServer) subscribePatients(conn *model.Connection, service *model.Service, active bool, patients []model.Patient) (*openkv.UpdateSubscriptionsResponse, error) {
|
|
client, err := getAuthenticatedClient(conn.Addr, conn.AuthConfig.Raw)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
subs := []*openkv.SubscriptionData{}
|
|
|
|
for _, pat := range patients {
|
|
subs = append(subs, &openkv.SubscriptionData{
|
|
Subscribe: active,
|
|
Subject: &openkv.PatientMeta{
|
|
ExternalId: pat.ExternalId,
|
|
ExternalIdSystem: "http://fhir.nl/fhir/NamingSystem/bsn",
|
|
Name: pat.Name,
|
|
Birthdate: pat.Birthdate,
|
|
},
|
|
ProtocolMeta: map[string]string{
|
|
"patientID": pat.PatientID,
|
|
},
|
|
})
|
|
}
|
|
|
|
req := &openkv.UpdateSubscriptionsRequest{
|
|
ServiceId: service.ServiceID,
|
|
SubscriptionData: subs,
|
|
}
|
|
|
|
resp, err := client.UpdateSubscriptions(context.Background(), req)
|
|
|
|
if err != nil {
|
|
log.Printf("Err in request: %v", err)
|
|
return nil, err
|
|
} else if !resp.Success {
|
|
log.Printf("success: %v; Err: %v", resp.Success, resp.Errors)
|
|
return nil, err
|
|
}
|
|
|
|
return resp, nil
|
|
}
|
|
|
|
func listSubscriptions(addr, service string) {
|
|
client, err := getAuthenticatedClient(addr, CONN_PSK)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
req := &openkv.ListSubscriptionsRequest{
|
|
ServiceId: service,
|
|
}
|
|
|
|
if resp, err := client.ListSubscriptions(context.Background(), req); err != nil {
|
|
log.Printf("Err in request: %v", err)
|
|
return
|
|
} else {
|
|
if !resp.Success {
|
|
log.Printf("success: %v; Err: %v", resp.Success, resp.Error)
|
|
} else {
|
|
log.Printf("success: %v", resp)
|
|
}
|
|
return
|
|
}
|
|
}
|
|
|