Skip to content

Commit

Permalink
refactor: move some logic into infra layers
Browse files Browse the repository at this point in the history
Separation of concerns led to the following:
-  Move patch and get patient logic to infrastructure layer
-  Move get episode of care login to infrastructure layer
  • Loading branch information
Salaton committed Jan 30, 2023
1 parent 03c0dc1 commit d2a5df7
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 112 deletions.
50 changes: 44 additions & 6 deletions pkg/clinical/infrastructure/datastore/cloudhealthcare/fhir.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ const (
)

var (
patientResourceType = "Patient"
patientResourceType = "Patient"
episodeOfCareResourceType = "EpisodeOfCare"
)

// StoreImpl represents the FHIR infrastructure implementation
Expand Down Expand Up @@ -1478,21 +1479,19 @@ func (fh *StoreImpl) DeleteFHIRObservation(ctx context.Context, id string) (bool

// GetFHIRPatient retrieves instances of FHIRPatient by ID
func (fh *StoreImpl) GetFHIRPatient(ctx context.Context, id string) (*domain.FHIRPatientRelayPayload, error) {

resourceType := "Patient"
var resource domain.FHIRPatient

data, err := fh.Dataset.GetFHIRResource(resourceType, id)
data, err := fh.Dataset.GetFHIRResource(patientResourceType, id)
if err != nil {
utils.ReportErrorToSentry(err)
return nil, fmt.Errorf("unable to get %s with ID %s, err: %s", resourceType, id, err)
return nil, fmt.Errorf("unable to get %s with ID %s, err: %s", patientResourceType, id, err)
}

err = json.Unmarshal(data, &resource)
if err != nil {
utils.ReportErrorToSentry(err)
return nil, fmt.Errorf(
"unable to unmarshal %s data from JSON, err: %v", resourceType, err)
"unable to unmarshal %s data from JSON, err: %v", patientResourceType, err)
}

hasOpenEpisodes, err := fh.HasOpenEpisode(ctx, resource)
Expand Down Expand Up @@ -1831,6 +1830,45 @@ func (fh *StoreImpl) CreateFHIRPatient(ctx context.Context, input domain.FHIRPat
return output, nil
}

// PatchFHIRPatient is used to patch a patient resource
func (fh *StoreImpl) PatchFHIRPatient(ctx context.Context, id string, params []map[string]interface{}) (*domain.FHIRPatient, error) {
resource := domain.FHIRPatient{}

data, err := fh.Dataset.PatchFHIRResource(patientResourceType, id, params)
if err != nil {
return nil, fmt.Errorf("unable to patch %s resource: %v", patientResourceType, err)
}

err = json.Unmarshal(data, &resource)
if err != nil {
return nil, fmt.Errorf(
"unable to unmarshal %s response JSON: data: %v\n, error: %v",
patientResourceType, string(data), err)
}
return &resource, nil
}

// UpdateFHIREpisodeOfCare updates a fhir episode of care
func (fh *StoreImpl) UpdateFHIREpisodeOfCare(ctx context.Context, fhirResourceID string, payload map[string]interface{}) (*domain.FHIREpisodeOfCare, error) {
resource := domain.FHIREpisodeOfCare{}

if fhirResourceID == "" {
return nil, fmt.Errorf("can't update with a nil ID")
}

data, err := fh.Dataset.UpdateFHIRResource(episodeOfCareResourceType, fhirResourceID, payload)
if err != nil {
return nil, fmt.Errorf("unable to update %s resource: %w", episodeOfCareResourceType, err)
}
err = json.Unmarshal(data, &resource)
if err != nil {
return nil, fmt.Errorf(
"unable to unmarshal %s response JSON: data: %v\n, error: %v",
episodeOfCareResourceType, string(data), err)
}
return &resource, nil
}

// CreateFHIRResource creates a FHIR resource
func (fh *StoreImpl) CreateFHIRResource(resourceType string, payload map[string]interface{}) ([]byte, error) {
return fh.Dataset.CreateFHIRResource(resourceType, payload)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,8 @@ type FHIRMock struct {
MockFHIRHeadersFn func() (http.Header, error)
MockGetBearerTokenFn func() (string, error)
MockCreateFHIRPatientFn func(ctx context.Context, input domain.FHIRPatientInput) (*domain.PatientPayload, error)

MockPatchFHIRResourceFn func(resourceType, fhirResourceID string, payload []map[string]interface{}) ([]byte, error)
MockUpdateFHIRResourceFn func(resourceType, fhirResourceID string, payload map[string]interface{}) ([]byte, error)
MockGetFHIRResourceFn func(resourceType, fhirResourceID string) ([]byte, error)
MockPatchFHIRPatientFn func(ctx context.Context, id string, params []map[string]interface{}) (*domain.FHIRPatient, error)
MockUpdateFHIREpisodeOfCareFn func(ctx context.Context, fhirResourceID string, payload map[string]interface{}) (*domain.FHIREpisodeOfCare, error)
}

// NewFHIRMock initializes a new instance of FHIR mock
Expand Down Expand Up @@ -429,45 +427,45 @@ func NewFHIRMock() *FHIRMock {
OpenEpisodes: []*domain.FHIREpisodeOfCare{},
}, nil
},

MockPatchFHIRResourceFn: func(resourceType, fhirResourceID string, payload []map[string]interface{}) ([]byte, error) {
m := map[string]interface{}{
"key": "value",
}
bs, err := json.Marshal(m)
if err != nil {
return nil, fmt.Errorf("unable to marshal map to JSON: %w", err)
}
return bs, nil
},
MockUpdateFHIRResourceFn: func(resourceType, fhirResourceID string, payload map[string]interface{}) ([]byte, error) {
m := map[string]interface{}{
"key": "value",
}
bs, err := json.Marshal(m)
if err != nil {
return nil, fmt.Errorf("unable to marshal map to JSON: %w", err)
}
return bs, nil
MockPatchFHIRPatientFn: func(ctx context.Context, id string, params []map[string]interface{}) (*domain.FHIRPatient, error) {
return &domain.FHIRPatient{
ID: new(string),
Text: &domain.FHIRNarrative{},
Identifier: []*domain.FHIRIdentifier{},
Active: new(bool),
Name: []*domain.FHIRHumanName{},
Telecom: []*domain.FHIRContactPoint{},
BirthDate: &scalarutils.Date{},
DeceasedBoolean: new(bool),
DeceasedDateTime: &scalarutils.Date{},
Address: []*domain.FHIRAddress{},
MaritalStatus: &domain.FHIRCodeableConcept{},
MultipleBirthBoolean: new(bool),
MultipleBirthInteger: new(string),
Photo: []*domain.FHIRAttachment{},
Contact: []*domain.FHIRPatientContact{},
Communication: []*domain.FHIRPatientCommunication{},
GeneralPractitioner: []*domain.FHIRReference{},
ManagingOrganization: &domain.FHIRReference{},
Link: []*domain.FHIRPatientLink{},
}, nil
},
MockGetFHIRResourceFn: func(resourceType, fhirResourceID string) ([]byte, error) {
n := map[string]interface{}{"given": []string{"John"}, "family": []string{"Doe"}}
p := map[string]interface{}{
"resourceType": "Patient/",
"id": "test-UUID",
"name": []map[string]interface{}{n},
}
m := map[string]interface{}{
"resourceType": "Patient/",
"status": "active",
"id": "test-UUID",
"patientRecord": p,
}
bs, err := json.Marshal(m)
if err != nil {
return nil, fmt.Errorf("unable to marshal map to JSON: %w", err)
}
return bs, nil
MockUpdateFHIREpisodeOfCareFn: func(ctx context.Context, fhirResourceID string, payload map[string]interface{}) (*domain.FHIREpisodeOfCare, error) {
return &domain.FHIREpisodeOfCare{
ID: new(string),
Text: &domain.FHIRNarrative{},
Identifier: []*domain.FHIRIdentifier{},
StatusHistory: []*domain.FHIREpisodeofcareStatushistory{},
Type: []*domain.FHIRCodeableConcept{},
Diagnosis: []*domain.FHIREpisodeofcareDiagnosis{},
Patient: &domain.FHIRReference{},
ManagingOrganization: &domain.FHIRReference{},
Period: &domain.FHIRPeriod{},
ReferralRequest: []*domain.FHIRReference{},
CareManager: &domain.FHIRReference{},
Team: []*domain.FHIRReference{},
Account: []*domain.FHIRReference{},
}, nil
},
}
}
Expand Down Expand Up @@ -718,17 +716,12 @@ func (fh *FHIRMock) CreateFHIRPatient(ctx context.Context, input domain.FHIRPati
return fh.MockCreateFHIRPatientFn(ctx, input)
}

// PatchFHIRResource mocks the implementation for patching a fhir resource
func (fh *FHIRMock) PatchFHIRResource(resourceType, fhirResourceID string, payload []map[string]interface{}) ([]byte, error) {
return fh.MockPatchFHIRResourceFn(resourceType, fhirResourceID, payload)
}

// UpdateFHIRResource mocks the implementation for updating a FHIR resource
func (fh *FHIRMock) UpdateFHIRResource(resourceType, fhirResourceID string, payload map[string]interface{}) ([]byte, error) {
return fh.MockUpdateFHIRResourceFn(resourceType, fhirResourceID, payload)
// PatchFHIRPatient mocks the implementation for patching a fhir patient
func (fh *FHIRMock) PatchFHIRPatient(ctx context.Context, id string, params []map[string]interface{}) (*domain.FHIRPatient, error) {
return fh.MockPatchFHIRPatientFn(ctx, id, params)
}

// GetFHIRResource mocks the implementation of getting a FHIR resource
func (fh *FHIRMock) GetFHIRResource(resourceType, fhirResourceID string) ([]byte, error) {
return fh.MockGetFHIRResourceFn(resourceType, fhirResourceID)
// UpdateFHIREpisodeOfCare mocks the implementation of updating a FHIR episode of care
func (fh *FHIRMock) UpdateFHIREpisodeOfCare(ctx context.Context, fhirResourceID string, payload map[string]interface{}) (*domain.FHIREpisodeOfCare, error) {
return fh.MockUpdateFHIREpisodeOfCareFn(ctx, fhirResourceID, payload)
}
6 changes: 2 additions & 4 deletions pkg/clinical/repository/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
// FHIR represents all the FHIR logic
type FHIR interface {
CreateEpisodeOfCare(ctx context.Context, episode domain.FHIREpisodeOfCare) (*domain.EpisodeOfCarePayload, error)
UpdateFHIREpisodeOfCare(ctx context.Context, fhirResourceID string, payload map[string]interface{}) (*domain.FHIREpisodeOfCare, error)
SearchFHIRCondition(ctx context.Context, params map[string]interface{}) (*domain.FHIRConditionRelayConnection, error)
CreateFHIRCondition(ctx context.Context, input domain.FHIRConditionInput) (*domain.FHIRConditionRelayPayload, error)
CreateFHIROrganization(ctx context.Context, input domain.FHIROrganizationInput) (*domain.FHIROrganizationRelayPayload, error)
Expand Down Expand Up @@ -62,12 +63,9 @@ type FHIR interface {
CreateFHIRMedicationStatement(ctx context.Context, input domain.FHIRMedicationStatementInput) (*domain.FHIRMedicationStatementRelayPayload, error)
CreateFHIRMedication(ctx context.Context, input domain.FHIRMedicationInput) (*domain.FHIRMedicationRelayPayload, error)
SearchFHIRMedicationStatement(ctx context.Context, params map[string]interface{}) (*domain.FHIRMedicationStatementRelayConnection, error)

CreateFHIRPatient(ctx context.Context, input domain.FHIRPatientInput) (*domain.PatientPayload, error)
PatchFHIRPatient(ctx context.Context, id string, params []map[string]interface{}) (*domain.FHIRPatient, error)

PatchFHIRResource(resourceType, fhirResourceID string, payload []map[string]interface{}) ([]byte, error)
UpdateFHIRResource(resourceType, fhirResourceID string, payload map[string]interface{}) ([]byte, error)
POSTRequest(resourceName string, path string, params url.Values, body io.Reader) ([]byte, error)
GetFHIRResource(resourceType, fhirResourceID string) ([]byte, error)
FHIRHeaders() (http.Header, error)
}
10 changes: 4 additions & 6 deletions pkg/clinical/usecases/clinical/fhir.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,15 +334,13 @@ func (c *UseCasesClinicalImpl) UpgradeEpisode(ctx context.Context, input domain.
return nil, fmt.Errorf("unable to turn episode of care input into a map: %v", err)
}

_, err = c.infrastructure.FHIR.UpdateFHIRResource(
"EpisodeOfCare", *episode.ID, payload)
episodeOfCare, err := c.infrastructure.FHIR.UpdateFHIREpisodeOfCare(ctx, *episode.ID, payload)
if err != nil {
utils.ReportErrorToSentry(err)
return nil, fmt.Errorf(
"unable to update episode of care resource: %v", err)
return nil, err
}

return &domain.EpisodeOfCarePayload{
EpisodeOfCare: episode,
EpisodeOfCare: episodeOfCare,
TotalVisits: len(encounters),
}, nil
}
Expand Down
56 changes: 13 additions & 43 deletions pkg/clinical/usecases/clinical/patient.go
Original file line number Diff line number Diff line change
Expand Up @@ -610,27 +610,21 @@ func (c *UseCasesClinicalImpl) FindPatientByID(ctx context.Context, id string) (
return nil, fmt.Errorf("patient ID cannot be empty")
}

data, err := c.infrastructure.FHIR.GetFHIRResource("Patient", id)
patient, err := c.infrastructure.FHIR.GetFHIRPatient(ctx, id)
if err != nil {
return nil, fmt.Errorf(
"unable to get patient with ID %s, err: %v", id, err)
}
var patient domain.FHIRPatient
err = json.Unmarshal(data, &patient)
if err != nil {
utils.ReportErrorToSentry(err)
return nil, fmt.Errorf(
"unable to unmarshal patient data from JSON, err: %v", err)
}
patientReference := fmt.Sprintf("Patient/%s", *patient.ID)

patientReference := fmt.Sprintf("Patient/%s", *patient.Resource.ID)
openEpisodes, err := c.infrastructure.FHIR.OpenEpisodes(ctx, patientReference)
if err != nil {
utils.ReportErrorToSentry(err)
return nil, fmt.Errorf(
"unable to get open episodes for %s, err: %v", patientReference, err)
}
return &domain.PatientPayload{
PatientRecord: &patient,
PatientRecord: patient.Resource,
OpenEpisodes: openEpisodes,
HasOpenEpisodes: len(openEpisodes) > 0,
}, nil
Expand Down Expand Up @@ -736,19 +730,11 @@ func (c *UseCasesClinicalImpl) UpdatePatient(ctx context.Context, input domain.S
patches = append(patches, patch)
}

data, err := c.infrastructure.FHIR.PatchFHIRResource("Patient", input.ID, patches)
patient, err := c.infrastructure.FHIR.PatchFHIRPatient(ctx, input.ID, patches)
if err != nil {
utils.ReportErrorToSentry(err)
return nil, fmt.Errorf("UpdatePatient: %v", err)
}
patient := domain.FHIRPatient{}
err = json.Unmarshal(data, &patient)
if err != nil {
utils.ReportErrorToSentry(err)
return nil, fmt.Errorf(
"unable to unmarshal patient response JSON: data: %v\n, error: %v",
string(data), err)
}
patientReference := fmt.Sprintf("Patient/%s", *patient.ID)
openEpisodes, err := c.infrastructure.FHIR.OpenEpisodes(ctx, patientReference)
if err != nil {
Expand All @@ -757,7 +743,7 @@ func (c *UseCasesClinicalImpl) UpdatePatient(ctx context.Context, input domain.S
"unable to get open episodes for %s, err: %v", patientReference, err)
}
return &domain.PatientPayload{
PatientRecord: &patient,
PatientRecord: patient,
OpenEpisodes: openEpisodes,
HasOpenEpisodes: len(openEpisodes) > 0,
}, nil
Expand Down Expand Up @@ -838,20 +824,12 @@ func (c *UseCasesClinicalImpl) AddNextOfKin(ctx context.Context, input domain.Si
},
}

data, err := c.infrastructure.FHIR.PatchFHIRResource(
"Patient", input.PatientID, patches)
patient, err := c.infrastructure.FHIR.PatchFHIRPatient(ctx, input.PatientID, patches)
if err != nil {
utils.ReportErrorToSentry(err)
return nil, fmt.Errorf("UpdatePatient: %v", err)
}
patient := domain.FHIRPatient{}
err = json.Unmarshal(data, &patient)
if err != nil {
utils.ReportErrorToSentry(err)
return nil, fmt.Errorf(
"unable to unmarshal patient response JSON: data: %v\n, error: %v",
string(data), err)
}

patientReference := fmt.Sprintf("Patient/%s", *patient.ID)
openEpisodes, err := c.infrastructure.FHIR.OpenEpisodes(ctx, patientReference)
if err != nil {
Expand All @@ -861,7 +839,7 @@ func (c *UseCasesClinicalImpl) AddNextOfKin(ctx context.Context, input domain.Si
}

return &domain.PatientPayload{
PatientRecord: &patient,
PatientRecord: patient,
OpenEpisodes: openEpisodes,
HasOpenEpisodes: len(openEpisodes) > 0,
}, nil
Expand Down Expand Up @@ -922,20 +900,12 @@ func (c *UseCasesClinicalImpl) AddNHIF(ctx context.Context, input *domain.Simple
},
}

data, err := c.infrastructure.FHIR.PatchFHIRResource(
"Patient", input.PatientID, patches)
patient, err := c.infrastructure.FHIR.PatchFHIRPatient(ctx, input.PatientID, patches)
if err != nil {
utils.ReportErrorToSentry(err)
return nil, fmt.Errorf("UpdatePatient: %v", err)
}
patient := domain.FHIRPatient{}
err = json.Unmarshal(data, &patient)
if err != nil {
utils.ReportErrorToSentry(err)
return nil, fmt.Errorf(
"unable to unmarshal patient response JSON: data: %v\n, error: %v",
string(data), err)
}

patientReference := fmt.Sprintf("Patient/%s", *patient.ID)
openEpisodes, err := c.infrastructure.FHIR.OpenEpisodes(ctx, patientReference)
if err != nil {
Expand All @@ -944,7 +914,7 @@ func (c *UseCasesClinicalImpl) AddNHIF(ctx context.Context, input *domain.Simple
"unable to get open episodes for %s, err: %v", patientReference, err)
}
return &domain.PatientPayload{
PatientRecord: &patient,
PatientRecord: patient,
OpenEpisodes: openEpisodes,
HasOpenEpisodes: len(openEpisodes) > 0,
}, nil
Expand Down Expand Up @@ -999,7 +969,7 @@ func (c *UseCasesClinicalImpl) CreateUpdatePatientExtraInformation(
return false, fmt.Errorf("an error occurred: %v", err)
}

_, err = c.infrastructure.FHIR.PatchFHIRResource("Patient", input.PatientID, patches)
_, err = c.infrastructure.FHIR.PatchFHIRPatient(ctx, input.PatientID, patches)
if err != nil {
utils.ReportErrorToSentry(err)
return false, fmt.Errorf("UpdatePatient: %v", err)
Expand Down

0 comments on commit d2a5df7

Please sign in to comment.