From d647cc939d149ea07448a7fd08163735b20dd9eb Mon Sep 17 00:00:00 2001 From: Muchogo Date: Wed, 8 Mar 2023 09:14:36 +0300 Subject: [PATCH] chore: additional linters to improve code quality linters: - ireturn - importas - repository interface improvement --- .golangci.yaml | 2 + .../application/extensions/extension.go | 4 +- pkg/clinical/domain/patient.go | 4 +- .../datastore/cloudhealthcare/fhir.go | 39 +- .../cloudhealthcare/fhir_unit_test.go | 359 +++++++++--------- .../cloudhealthcare/fhirdataset/dataset.go | 5 +- .../fhirdataset/dataset_unit_test.go | 1 - pkg/clinical/infrastructure/infrastructure.go | 23 +- .../services/openconceptlab/service.go | 13 - .../infrastructure/services/pubsub/service.go | 17 +- .../services/pubsub/subscriber.go | 18 +- .../presentation/interactor/interactor.go | 44 ++- pkg/clinical/presentation/rest/handlers.go | 2 +- pkg/clinical/repository/repository.go | 110 ++++-- pkg/clinical/usecases/clinical/patient.go | 17 +- pkg/clinical/usecases/config_test.go | 3 +- pkg/clinical/usecases/ocl/ocl.go | 13 - pkg/clinical/usecases/usecases.go | 37 +- 18 files changed, 396 insertions(+), 315 deletions(-) delete mode 100644 pkg/clinical/infrastructure/datastore/cloudhealthcare/fhirdataset/dataset_unit_test.go diff --git a/.golangci.yaml b/.golangci.yaml index 05ee5f00..f58fb7b3 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -26,6 +26,8 @@ linters: - contextcheck - gocritic - nilerr + - ireturn + - importas linters-settings: staticcheck: diff --git a/pkg/clinical/application/extensions/extension.go b/pkg/clinical/application/extensions/extension.go index 82860b6d..7f4a46a9 100644 --- a/pkg/clinical/application/extensions/extension.go +++ b/pkg/clinical/application/extensions/extension.go @@ -24,7 +24,7 @@ type ISCClientExtension interface { type ISCExtensionImpl struct{} // NewISCExtension initializes an ISC extension -func NewISCExtension() ISCClientExtension { +func NewISCExtension() *ISCExtensionImpl { return &ISCExtensionImpl{} } @@ -59,7 +59,7 @@ type BaseExtensionImpl struct { } // NewBaseExtensionImpl ... -func NewBaseExtensionImpl() BaseExtension { +func NewBaseExtensionImpl() *BaseExtensionImpl { return &BaseExtensionImpl{} } diff --git a/pkg/clinical/domain/patient.go b/pkg/clinical/domain/patient.go index c59648a8..d84efb89 100644 --- a/pkg/clinical/domain/patient.go +++ b/pkg/clinical/domain/patient.go @@ -312,8 +312,8 @@ type PatientLinkEdge struct { func (pl *PatientLink) IsNode() {} // GetID returns the patient links primary key -func (pl *PatientLink) GetID() firebasetools.ID { - return firebasetools.IDValue(pl.ID) +func (pl *PatientLink) GetID() string { + return pl.ID } // SetID sets the patient links' id diff --git a/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhir.go b/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhir.go index 7d37d6d7..52cf475e 100644 --- a/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhir.go +++ b/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhir.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "io" - "net/http" "net/url" "time" @@ -14,7 +13,6 @@ import ( "github.com/savannahghi/clinical/pkg/clinical/application/common/helpers" "github.com/savannahghi/clinical/pkg/clinical/domain" dataset "github.com/savannahghi/clinical/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhirdataset" - "github.com/savannahghi/clinical/pkg/clinical/repository" "github.com/savannahghi/converterandformatter" "github.com/savannahghi/firebasetools" "github.com/savannahghi/scalarutils" @@ -28,6 +26,7 @@ const ( // resource types const ( + organizationResource = "Organization" patientResourceType = "Patient" episodeOfCareResourceType = "EpisodeOfCare" observationResourceType = "Observation" @@ -38,6 +37,7 @@ const ( encounterResourceType = "Encounter" compositionResourceType = "Composition" medicationStatementResourceType = "MedicationStatement" + medicationResourceType = "Medication" ) // StoreImpl represents the FHIR infrastructure implementation @@ -48,7 +48,7 @@ type StoreImpl struct { // NewFHIRStoreImpl initializes the new FHIR implementation func NewFHIRStoreImpl( dataset dataset.FHIRRepository, -) repository.FHIR { +) *StoreImpl { return &StoreImpl{ Dataset: dataset, } @@ -234,23 +234,22 @@ func (fh StoreImpl) CreateFHIRCondition(ctx context.Context, input domain.FHIRCo // CreateFHIROrganization creates a FHIROrganization instance func (fh StoreImpl) CreateFHIROrganization(ctx context.Context, input domain.FHIROrganizationInput) (*domain.FHIROrganizationRelayPayload, error) { - resourceType := "Organization" resource := domain.FHIROrganization{} payload, err := converterandformatter.StructToMap(input) if err != nil { - return nil, fmt.Errorf("unable to turn %s input into a map: %w", resourceType, err) + return nil, fmt.Errorf("unable to turn %s input into a map: %w", organizationResource, err) } - data, err := fh.Dataset.CreateFHIRResource(resourceType, payload) + data, err := fh.Dataset.CreateFHIRResource(organizationResource, payload) if err != nil { - return nil, fmt.Errorf("unable to create %s resource: %w", resourceType, err) + return nil, fmt.Errorf("unable to create %s resource: %w", organizationResource, err) } err = json.Unmarshal(data, &resource) if err != nil { return nil, fmt.Errorf( "unable to unmarshal %s response JSON: data: %v\n, error: %w", - resourceType, string(data), err) + organizationResource, string(data), err) } output := &domain.FHIROrganizationRelayPayload{ @@ -261,10 +260,8 @@ func (fh StoreImpl) CreateFHIROrganization(ctx context.Context, input domain.FHI // SearchFHIROrganization provides a search API for FHIROrganization func (fh StoreImpl) SearchFHIROrganization(ctx context.Context, params map[string]interface{}) (*domain.FHIROrganizationRelayConnection, error) { - - resourceName := "Organization" output := domain.FHIROrganizationRelayConnection{} - resources, err := fh.searchFilterHelper(ctx, resourceName, params) + resources, err := fh.searchFilterHelper(ctx, organizationResource, params) if err != nil { return nil, err } @@ -280,7 +277,7 @@ func (fh StoreImpl) SearchFHIROrganization(ctx context.Context, params map[strin err = json.Unmarshal(resourceBs, &resource) if err != nil { return nil, fmt.Errorf( - "server error: Unable to unmarshal %s: %w", resourceName, err) + "server error: Unable to unmarshal %s: %w", organizationResource, err) } output.Edges = append(output.Edges, &domain.FHIROrganizationRelayEdge{ Node: &resource, @@ -295,7 +292,7 @@ func (fh StoreImpl) FindOrganizationByID(ctx context.Context, organizationID str return nil, fmt.Errorf("organization ID is required") } - data, err := fh.Dataset.GetFHIRResource("Organization", organizationID) + data, err := fh.Dataset.GetFHIRResource(organizationResource, organizationID) if err != nil { return nil, fmt.Errorf("unable to retrieve organization: %w", err) } @@ -1447,25 +1444,23 @@ func (fh *StoreImpl) CreateFHIRMedicationStatement(ctx context.Context, input do // CreateFHIRMedication creates a new FHIR Medication instance func (fh *StoreImpl) CreateFHIRMedication(ctx context.Context, input domain.FHIRMedicationInput) (*domain.FHIRMedicationRelayPayload, error) { - resourceType := "Medication" - resource := domain.FHIRMedication{} payload, err := converterandformatter.StructToMap(input) if err != nil { - return nil, fmt.Errorf("unable to turn %s input into a map: %w", resourceType, err) + return nil, fmt.Errorf("unable to turn %s input into a map: %w", medicationResourceType, err) } - data, err := fh.Dataset.CreateFHIRResource(resourceType, payload) + data, err := fh.Dataset.CreateFHIRResource(medicationResourceType, payload) if err != nil { - return nil, fmt.Errorf("unable to create/update %s resource: %w", resourceType, err) + return nil, fmt.Errorf("unable to create/update %s resource: %w", medicationResourceType, err) } err = json.Unmarshal(data, &resource) if err != nil { return nil, fmt.Errorf( "unable to unmarshal %s response JSON: data: %v\n, error: %w", - resourceType, string(data), err) + medicationResourceType, string(data), err) } output := &domain.FHIRMedicationRelayPayload{ @@ -1668,9 +1663,3 @@ func (fh *StoreImpl) POSTRequest(resourceName string, path string, params url.Va func (fh *StoreImpl) GetFHIRResource(resourceType, fhirResourceID string) ([]byte, error) { return fh.Dataset.GetFHIRResource(resourceType, fhirResourceID) } - -// FHIRHeaders composes suitable FHIR headers, with authentication and content -// type already set -func (fh *StoreImpl) FHIRHeaders() (http.Header, error) { - return fh.Dataset.FHIRHeaders() -} diff --git a/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhir_unit_test.go b/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhir_unit_test.go index e6dceb13..8114a1cb 100644 --- a/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhir_unit_test.go +++ b/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhir_unit_test.go @@ -18,37 +18,12 @@ import ( ) func TestStoreImpl_CreateEpisodeOfCare_Unittest(t *testing.T) { - ctx := context.Background() - var d = fakeDataset.NewFakeFHIRRepositoryMock() - - h := FHIR.NewFHIRStoreImpl(d) UUID := uuid.New().String() PatientRef := "Patient/1" OrgRef := "Organization/1" status := domain.EpisodeOfCareStatusEnumFinished - episode := &domain.FHIREpisodeOfCare{ - ID: &UUID, - Text: &domain.FHIRNarrative{}, - Identifier: []*domain.FHIRIdentifier{}, - Status: &(status), - StatusHistory: []*domain.FHIREpisodeofcareStatushistory{}, - Type: []*domain.FHIRCodeableConcept{}, - Diagnosis: []*domain.FHIREpisodeofcareDiagnosis{}, - Patient: &domain.FHIRReference{ - Reference: &PatientRef, - }, - ManagingOrganization: &domain.FHIRReference{ - Reference: &OrgRef, - }, - Period: &domain.FHIRPeriod{}, - ReferralRequest: []*domain.FHIRReference{}, - CareManager: &domain.FHIRReference{}, - Team: []*domain.FHIRReference{}, - Account: []*domain.FHIRReference{}, - } - type args struct { ctx context.Context episode domain.FHIREpisodeOfCare @@ -61,8 +36,27 @@ func TestStoreImpl_CreateEpisodeOfCare_Unittest(t *testing.T) { { name: "Happy case", args: args{ - ctx: ctx, - episode: *episode, + ctx: context.Background(), + episode: domain.FHIREpisodeOfCare{ + ID: &UUID, + Text: &domain.FHIRNarrative{}, + Identifier: []*domain.FHIRIdentifier{}, + Status: &(status), + StatusHistory: []*domain.FHIREpisodeofcareStatushistory{}, + Type: []*domain.FHIRCodeableConcept{}, + Diagnosis: []*domain.FHIREpisodeofcareDiagnosis{}, + Patient: &domain.FHIRReference{ + Reference: &PatientRef, + }, + ManagingOrganization: &domain.FHIRReference{ + Reference: &OrgRef, + }, + Period: &domain.FHIRPeriod{}, + ReferralRequest: []*domain.FHIRReference{}, + CareManager: &domain.FHIRReference{}, + Team: []*domain.FHIRReference{}, + Account: []*domain.FHIRReference{}, + }, }, wantErr: false, }, @@ -70,7 +64,7 @@ func TestStoreImpl_CreateEpisodeOfCare_Unittest(t *testing.T) { { name: "Sad case", args: args{ - ctx: ctx, + ctx: context.Background(), episode: domain.FHIREpisodeOfCare{ ID: nil, Text: &domain.FHIRNarrative{}, @@ -96,6 +90,9 @@ func TestStoreImpl_CreateEpisodeOfCare_Unittest(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + var d = fakeDataset.NewFakeFHIRRepositoryMock() + + h := FHIR.NewFHIRStoreImpl(d) if tt.name == "Sad case" { d.MockCreateFHIRResourceFn = func(resourceType string, payload map[string]interface{}) ([]byte, error) { @@ -112,55 +109,10 @@ func TestStoreImpl_CreateEpisodeOfCare_Unittest(t *testing.T) { } func TestStoreImpl_CreateFHIRCondition(t *testing.T) { - ctx := context.Background() - var d = fakeDataset.NewFakeFHIRRepositoryMock() - - h := FHIR.NewFHIRStoreImpl(d) ID := uuid.New().String() - fhirconditionInput := &domain.FHIRConditionInput{ - ID: &ID, - Identifier: []*domain.FHIRIdentifierInput{}, - ClinicalStatus: &domain.FHIRCodeableConceptInput{}, - VerificationStatus: &domain.FHIRCodeableConceptInput{}, - Category: []*domain.FHIRCodeableConceptInput{}, - Severity: &domain.FHIRCodeableConceptInput{}, - Code: &domain.FHIRCodeableConceptInput{}, - BodySite: []*domain.FHIRCodeableConceptInput{}, - Subject: &domain.FHIRReferenceInput{}, - Encounter: &domain.FHIRReferenceInput{}, - OnsetDateTime: &scalarutils.Date{ - Year: 2000, - Month: 3, - Day: 30, - }, - OnsetAge: &domain.FHIRAgeInput{}, - OnsetPeriod: &domain.FHIRPeriodInput{}, - OnsetRange: &domain.FHIRRangeInput{}, - OnsetString: new(string), - AbatementDateTime: &scalarutils.Date{ - Year: 2000, - Month: 3, - Day: 30, - }, - AbatementAge: &domain.FHIRAgeInput{}, - AbatementPeriod: &domain.FHIRPeriodInput{}, - AbatementRange: &domain.FHIRRangeInput{}, - AbatementString: new(string), - RecordedDate: &scalarutils.Date{ - Year: 2000, - Month: 3, - Day: 30, - }, - Recorder: &domain.FHIRReferenceInput{}, - Asserter: &domain.FHIRReferenceInput{}, - Stage: []*domain.FHIRConditionStageInput{}, - Evidence: []*domain.FHIRConditionEvidenceInput{}, - Note: []*domain.FHIRAnnotationInput{}, - } - - invalidfhirconditionInput := &domain.FHIRConditionInput{ + invalidfhirconditionInput := domain.FHIRConditionInput{ ID: nil, Identifier: []*domain.FHIRIdentifierInput{}, ClinicalStatus: &domain.FHIRCodeableConceptInput{}, @@ -182,8 +134,47 @@ func TestStoreImpl_CreateFHIRCondition(t *testing.T) { { name: "Happy case", args: args{ - ctx: ctx, - input: *fhirconditionInput, + ctx: context.Background(), + input: domain.FHIRConditionInput{ + ID: &ID, + Identifier: []*domain.FHIRIdentifierInput{}, + ClinicalStatus: &domain.FHIRCodeableConceptInput{}, + VerificationStatus: &domain.FHIRCodeableConceptInput{}, + Category: []*domain.FHIRCodeableConceptInput{}, + Severity: &domain.FHIRCodeableConceptInput{}, + Code: &domain.FHIRCodeableConceptInput{}, + BodySite: []*domain.FHIRCodeableConceptInput{}, + Subject: &domain.FHIRReferenceInput{}, + Encounter: &domain.FHIRReferenceInput{}, + OnsetDateTime: &scalarutils.Date{ + Year: 2000, + Month: 3, + Day: 30, + }, + OnsetAge: &domain.FHIRAgeInput{}, + OnsetPeriod: &domain.FHIRPeriodInput{}, + OnsetRange: &domain.FHIRRangeInput{}, + OnsetString: new(string), + AbatementDateTime: &scalarutils.Date{ + Year: 2000, + Month: 3, + Day: 30, + }, + AbatementAge: &domain.FHIRAgeInput{}, + AbatementPeriod: &domain.FHIRPeriodInput{}, + AbatementRange: &domain.FHIRRangeInput{}, + AbatementString: new(string), + RecordedDate: &scalarutils.Date{ + Year: 2000, + Month: 3, + Day: 30, + }, + Recorder: &domain.FHIRReferenceInput{}, + Asserter: &domain.FHIRReferenceInput{}, + Stage: []*domain.FHIRConditionStageInput{}, + Evidence: []*domain.FHIRConditionEvidenceInput{}, + Note: []*domain.FHIRAnnotationInput{}, + }, }, wantErr: false, }, @@ -191,14 +182,17 @@ func TestStoreImpl_CreateFHIRCondition(t *testing.T) { { name: "Sad case", args: args{ - ctx: ctx, - input: *invalidfhirconditionInput, + ctx: context.Background(), + input: invalidfhirconditionInput, }, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + var d = fakeDataset.NewFakeFHIRRepositoryMock() + h := FHIR.NewFHIRStoreImpl(d) + if tt.name == "Sad case" { d.MockCreateFHIRResourceFn = func(resourceType string, payload map[string]interface{}) ([]byte, error) { return nil, fmt.Errorf("error") @@ -214,16 +208,12 @@ func TestStoreImpl_CreateFHIRCondition(t *testing.T) { } func TestStoreImpl_CreateFHIROrganization_Unittest(t *testing.T) { - ctx := context.Background() - var d = fakeDataset.NewFakeFHIRRepositoryMock() - - h := FHIR.NewFHIRStoreImpl(d) ID := ksuid.New().String() active := true testname := gofakeit.FirstName() - orgInput := &domain.FHIROrganizationInput{ + orgInput := domain.FHIROrganizationInput{ ID: &ID, Active: &active, Identifier: []*domain.FHIRIdentifierInput{}, @@ -234,7 +224,7 @@ func TestStoreImpl_CreateFHIROrganization_Unittest(t *testing.T) { Address: []*domain.FHIRAddressInput{}, } - invalidOrgInput := &domain.FHIROrganizationInput{ + invalidOrgInput := domain.FHIROrganizationInput{ ID: &ID, Active: new(bool), Identifier: []*domain.FHIRIdentifierInput{}, @@ -256,8 +246,8 @@ func TestStoreImpl_CreateFHIROrganization_Unittest(t *testing.T) { { name: "Happy case", args: args{ - ctx: ctx, - input: *orgInput, + ctx: context.Background(), + input: orgInput, }, wantErr: false, }, @@ -265,14 +255,18 @@ func TestStoreImpl_CreateFHIROrganization_Unittest(t *testing.T) { { name: "Sad case", args: args{ - ctx: ctx, - input: *invalidOrgInput, + ctx: context.Background(), + input: invalidOrgInput, }, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + var d = fakeDataset.NewFakeFHIRRepositoryMock() + + h := FHIR.NewFHIRStoreImpl(d) + if tt.name == "Sad case" { if tt.name == "Sad case" { d.MockCreateFHIRResourceFn = func(resourceType string, payload map[string]interface{}) ([]byte, error) { @@ -290,10 +284,6 @@ func TestStoreImpl_CreateFHIROrganization_Unittest(t *testing.T) { } func TestStoreImpl_FindOrganizationByID_Unittest(t *testing.T) { - ctx := context.Background() - var d = fakeDataset.NewFakeFHIRRepositoryMock() - - h := FHIR.NewFHIRStoreImpl(d) type args struct { ctx context.Context @@ -308,7 +298,7 @@ func TestStoreImpl_FindOrganizationByID_Unittest(t *testing.T) { { name: "Sad case", args: args{ - ctx: ctx, + ctx: context.Background(), organizationID: "", }, wantErr: true, @@ -316,6 +306,10 @@ func TestStoreImpl_FindOrganizationByID_Unittest(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + var d = fakeDataset.NewFakeFHIRRepositoryMock() + + h := FHIR.NewFHIRStoreImpl(d) + if tt.name == "Sad case" { d.MockGetFHIRResourceFn = func(resourceType string, id string) ([]byte, error) { return nil, fmt.Errorf("an error occurred") @@ -340,10 +334,6 @@ func TestStoreImpl_FindOrganizationByID_Unittest(t *testing.T) { } func TestStoreImpl_SearchEpisodesByParam(t *testing.T) { - ctx := context.Background() - var d = fakeDataset.NewFakeFHIRRepositoryMock() - - h := FHIR.NewFHIRStoreImpl(d) type args struct { ctx context.Context @@ -357,7 +347,7 @@ func TestStoreImpl_SearchEpisodesByParam(t *testing.T) { { name: "Happy case", args: args{ - ctx: ctx, + ctx: context.Background(), searchParams: map[string][]string{ "patient": {"12233"}, }, @@ -367,13 +357,17 @@ func TestStoreImpl_SearchEpisodesByParam(t *testing.T) { { name: "Sad case", args: args{ - ctx: ctx, + ctx: context.Background(), }, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + var d = fakeDataset.NewFakeFHIRRepositoryMock() + + h := FHIR.NewFHIRStoreImpl(d) + if tt.name == "Sad case" { d.MockPOSTRequestFn = func(resourceName, path string, params url.Values, body io.Reader) ([]byte, error) { return nil, fmt.Errorf("an error occurred") @@ -389,10 +383,6 @@ func TestStoreImpl_SearchEpisodesByParam(t *testing.T) { } func TestStoreImpl_SearchFHIREpisodeOfCare(t *testing.T) { - ctx := context.Background() - var d = fakeDataset.NewFakeFHIRRepositoryMock() - - h := FHIR.NewFHIRStoreImpl(d) type args struct { ctx context.Context @@ -406,7 +396,7 @@ func TestStoreImpl_SearchFHIREpisodeOfCare(t *testing.T) { { name: "Happy case", args: args{ - ctx: ctx, + ctx: context.Background(), params: map[string]interface{}{ "patient": "12233", }, @@ -416,7 +406,7 @@ func TestStoreImpl_SearchFHIREpisodeOfCare(t *testing.T) { { name: "Sad case", args: args{ - ctx: ctx, + ctx: context.Background(), params: map[string]interface{}{ "patient": "12233", }, @@ -426,6 +416,10 @@ func TestStoreImpl_SearchFHIREpisodeOfCare(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + var d = fakeDataset.NewFakeFHIRRepositoryMock() + + h := FHIR.NewFHIRStoreImpl(d) + if tt.name == "Sad case" { d.MockPOSTRequestFn = func(resourceName, path string, params url.Values, body io.Reader) ([]byte, error) { return nil, fmt.Errorf("an error occurred") @@ -441,10 +435,6 @@ func TestStoreImpl_SearchFHIREpisodeOfCare(t *testing.T) { } func TestStoreImpl_OpenEpisodes(t *testing.T) { - ctx := context.Background() - var d = fakeDataset.NewFakeFHIRRepositoryMock() - - h := FHIR.NewFHIRStoreImpl(d) patientReference := fmt.Sprintf("Patient/%s", ksuid.New().String()) @@ -460,7 +450,7 @@ func TestStoreImpl_OpenEpisodes(t *testing.T) { { name: "Happy case", args: args{ - ctx: ctx, + ctx: context.Background(), patientReference: patientReference, }, wantErr: false, @@ -468,7 +458,7 @@ func TestStoreImpl_OpenEpisodes(t *testing.T) { { name: "Sad case", args: args{ - ctx: ctx, + ctx: context.Background(), patientReference: patientReference, }, wantErr: true, @@ -476,6 +466,10 @@ func TestStoreImpl_OpenEpisodes(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + var d = fakeDataset.NewFakeFHIRRepositoryMock() + + h := FHIR.NewFHIRStoreImpl(d) + if tt.name == "Sad case" { d.MockPOSTRequestFn = func(resourceName, path string, params url.Values, body io.Reader) ([]byte, error) { return nil, fmt.Errorf("an error occurred") @@ -491,10 +485,6 @@ func TestStoreImpl_OpenEpisodes(t *testing.T) { } func TestStoreImpl_HasOpenEpisode(t *testing.T) { - ctx := context.Background() - var d = fakeDataset.NewFakeFHIRRepositoryMock() - - h := FHIR.NewFHIRStoreImpl(d) UUID := uuid.New().String() @@ -516,7 +506,7 @@ func TestStoreImpl_HasOpenEpisode(t *testing.T) { { name: "Happy case", args: args{ - ctx: ctx, + ctx: context.Background(), patient: patient, }, wantErr: false, @@ -525,7 +515,7 @@ func TestStoreImpl_HasOpenEpisode(t *testing.T) { { name: "Sad case", args: args{ - ctx: ctx, + ctx: context.Background(), patient: patient, }, wantErr: true, @@ -534,6 +524,10 @@ func TestStoreImpl_HasOpenEpisode(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + var d = fakeDataset.NewFakeFHIRRepositoryMock() + + h := FHIR.NewFHIRStoreImpl(d) + if tt.name == "Sad case" { d.MockPOSTRequestFn = func(resourceName, path string, params url.Values, body io.Reader) ([]byte, error) { return nil, fmt.Errorf("an error occurred") @@ -550,11 +544,6 @@ func TestStoreImpl_HasOpenEpisode(t *testing.T) { func TestStoreImpl_CreateFHIREncounter(t *testing.T) { - ctx := context.Background() - var d = fakeDataset.NewFakeFHIRRepositoryMock() - - h := FHIR.NewFHIRStoreImpl(d) - input := domain.FHIREncounterInput{} type args struct { ctx context.Context @@ -569,7 +558,7 @@ func TestStoreImpl_CreateFHIREncounter(t *testing.T) { { name: "Happy case", args: args{ - ctx: ctx, + ctx: context.Background(), input: input, }, wantErr: false, @@ -577,7 +566,7 @@ func TestStoreImpl_CreateFHIREncounter(t *testing.T) { { name: "Sad case", args: args{ - ctx: ctx, + ctx: context.Background(), input: input, }, wantErr: true, @@ -585,6 +574,10 @@ func TestStoreImpl_CreateFHIREncounter(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + var d = fakeDataset.NewFakeFHIRRepositoryMock() + + h := FHIR.NewFHIRStoreImpl(d) + if tt.name == "Sad case" { d.MockCreateFHIRResourceFn = func(resourceType string, payload map[string]interface{}) ([]byte, error) { return nil, fmt.Errorf("an error occurred") @@ -601,10 +594,6 @@ func TestStoreImpl_CreateFHIREncounter(t *testing.T) { } func TestStoreImpl_GetFHIREpisodeOfCare(t *testing.T) { - ctx := context.Background() - var d = fakeDataset.NewFakeFHIRRepositoryMock() - - h := FHIR.NewFHIRStoreImpl(d) id := ksuid.New().String() @@ -620,7 +609,7 @@ func TestStoreImpl_GetFHIREpisodeOfCare(t *testing.T) { { name: "Happy case", args: args{ - ctx: ctx, + ctx: context.Background(), id: id, }, wantErr: false, @@ -628,7 +617,7 @@ func TestStoreImpl_GetFHIREpisodeOfCare(t *testing.T) { { name: "Sad case", args: args{ - ctx: ctx, + ctx: context.Background(), id: id, }, wantErr: true, @@ -636,6 +625,10 @@ func TestStoreImpl_GetFHIREpisodeOfCare(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + var d = fakeDataset.NewFakeFHIRRepositoryMock() + + h := FHIR.NewFHIRStoreImpl(d) + if tt.name == "Sad case" { d.MockGetFHIRResourceFn = func(resourceType, fhirResourceID string) ([]byte, error) { return nil, fmt.Errorf("an error occurred") @@ -652,10 +645,6 @@ func TestStoreImpl_GetFHIREpisodeOfCare(t *testing.T) { } func TestClinicalUseCaseImpl_StartEncounter(t *testing.T) { - ctx := context.Background() - var d = fakeDataset.NewFakeFHIRRepositoryMock() - - h := FHIR.NewFHIRStoreImpl(d) episodeID := uuid.New().String() @@ -672,7 +661,7 @@ func TestClinicalUseCaseImpl_StartEncounter(t *testing.T) { { name: "Sad case", args: args{ - ctx: ctx, + ctx: context.Background(), episodeID: episodeID, }, wantErr: true, @@ -680,6 +669,10 @@ func TestClinicalUseCaseImpl_StartEncounter(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + var d = fakeDataset.NewFakeFHIRRepositoryMock() + + h := FHIR.NewFHIRStoreImpl(d) + if tt.name == "Sad case" { d.MockGetFHIRResourceFn = func(resourceType, fhirResourceID string) ([]byte, error) { return nil, fmt.Errorf("an error occurred") @@ -695,10 +688,6 @@ func TestClinicalUseCaseImpl_StartEncounter(t *testing.T) { } func TestStoreImpl_GetFHIREncounter(t *testing.T) { - ctx := context.Background() - var d = fakeDataset.NewFakeFHIRRepositoryMock() - - h := FHIR.NewFHIRStoreImpl(d) type args struct { ctx context.Context @@ -712,7 +701,7 @@ func TestStoreImpl_GetFHIREncounter(t *testing.T) { { name: "Happy case", args: args{ - ctx: ctx, + ctx: context.Background(), id: uuid.New().String(), }, wantErr: false, @@ -720,7 +709,7 @@ func TestStoreImpl_GetFHIREncounter(t *testing.T) { { name: "Sad case", args: args{ - ctx: ctx, + ctx: context.Background(), id: "", }, wantErr: true, @@ -728,6 +717,10 @@ func TestStoreImpl_GetFHIREncounter(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + var d = fakeDataset.NewFakeFHIRRepositoryMock() + + h := FHIR.NewFHIRStoreImpl(d) + if tt.name == "Sad case" { d.MockGetFHIRResourceFn = func(resourceType, fhirResourceID string) ([]byte, error) { return nil, fmt.Errorf("an error occurred") @@ -743,10 +736,6 @@ func TestStoreImpl_GetFHIREncounter(t *testing.T) { } func TestStoreImpl_CreateFHIRServiceRequest(t *testing.T) { - ctx := context.Background() - var d = fakeDataset.NewFakeFHIRRepositoryMock() - - h := FHIR.NewFHIRStoreImpl(d) UUID := uuid.New().String() @@ -762,7 +751,7 @@ func TestStoreImpl_CreateFHIRServiceRequest(t *testing.T) { { name: "Happy case", args: args{ - ctx: ctx, + ctx: context.Background(), input: domain.FHIRServiceRequestInput{ ID: &UUID, }, @@ -772,13 +761,17 @@ func TestStoreImpl_CreateFHIRServiceRequest(t *testing.T) { { name: "Sad case", args: args{ - ctx: ctx, + ctx: context.Background(), }, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + var d = fakeDataset.NewFakeFHIRRepositoryMock() + + h := FHIR.NewFHIRStoreImpl(d) + if tt.name == "Sad case" { d.MockCreateFHIRResourceFn = func(resourceType string, payload map[string]interface{}) ([]byte, error) { return nil, fmt.Errorf("an error occurred") @@ -794,10 +787,6 @@ func TestStoreImpl_CreateFHIRServiceRequest(t *testing.T) { } func TestStoreImpl_CreateFHIRAllergyIntolerance(t *testing.T) { - ctx := context.Background() - var d = fakeDataset.NewFakeFHIRRepositoryMock() - - h := FHIR.NewFHIRStoreImpl(d) UUID := uuid.New().String() @@ -813,7 +802,7 @@ func TestStoreImpl_CreateFHIRAllergyIntolerance(t *testing.T) { { name: "Happy case", args: args{ - ctx: ctx, + ctx: context.Background(), input: domain.FHIRAllergyIntoleranceInput{ ID: &UUID, }, @@ -823,13 +812,17 @@ func TestStoreImpl_CreateFHIRAllergyIntolerance(t *testing.T) { { name: "Sad case", args: args{ - ctx: ctx, + ctx: context.Background(), }, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + var d = fakeDataset.NewFakeFHIRRepositoryMock() + + h := FHIR.NewFHIRStoreImpl(d) + if tt.name == "Sad case" { d.MockCreateFHIRResourceFn = func(resourceType string, payload map[string]interface{}) ([]byte, error) { return nil, fmt.Errorf("an error occurred") @@ -845,10 +838,6 @@ func TestStoreImpl_CreateFHIRAllergyIntolerance(t *testing.T) { } func TestStoreImpl_UpdateFHIRAllergyIntolerance(t *testing.T) { - ctx := context.Background() - var d = fakeDataset.NewFakeFHIRRepositoryMock() - - h := FHIR.NewFHIRStoreImpl(d) UUID := uuid.New().String() @@ -869,7 +858,7 @@ func TestStoreImpl_UpdateFHIRAllergyIntolerance(t *testing.T) { { name: "Happy case", args: args{ - ctx: ctx, + ctx: context.Background(), input: input, }, wantErr: false, @@ -877,7 +866,7 @@ func TestStoreImpl_UpdateFHIRAllergyIntolerance(t *testing.T) { { name: "Sad case", args: args{ - ctx: ctx, + ctx: context.Background(), input: input, }, wantErr: true, @@ -885,6 +874,10 @@ func TestStoreImpl_UpdateFHIRAllergyIntolerance(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + var d = fakeDataset.NewFakeFHIRRepositoryMock() + + h := FHIR.NewFHIRStoreImpl(d) + if tt.name == "Sad case" { d.MockUpdateFHIRResourceFn = func(resourceType, fhirResourceID string, payload map[string]interface{}) ([]byte, error) { return nil, fmt.Errorf("an error occurred") @@ -900,10 +893,6 @@ func TestStoreImpl_UpdateFHIRAllergyIntolerance(t *testing.T) { } func TestStoreImpl_SearchFHIRComposition(t *testing.T) { - ctx := context.Background() - var d = fakeDataset.NewFakeFHIRRepositoryMock() - - h := FHIR.NewFHIRStoreImpl(d) params := map[string]interface{}{"test": "123"} @@ -920,7 +909,7 @@ func TestStoreImpl_SearchFHIRComposition(t *testing.T) { { name: "Happy case", args: args{ - ctx: ctx, + ctx: context.Background(), params: params, }, wantErr: false, @@ -928,7 +917,7 @@ func TestStoreImpl_SearchFHIRComposition(t *testing.T) { { name: "Sad case", args: args{ - ctx: ctx, + ctx: context.Background(), params: params, }, wantErr: true, @@ -936,6 +925,10 @@ func TestStoreImpl_SearchFHIRComposition(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + var d = fakeDataset.NewFakeFHIRRepositoryMock() + + h := FHIR.NewFHIRStoreImpl(d) + if tt.name == "Sad case" { d.MockPOSTRequestFn = func(resourceName, path string, params url.Values, body io.Reader) ([]byte, error) { return nil, fmt.Errorf("an error occurred") @@ -952,10 +945,6 @@ func TestStoreImpl_SearchFHIRComposition(t *testing.T) { } func TestStoreImpl_CreateFHIRComposition(t *testing.T) { - ctx := context.Background() - var d = fakeDataset.NewFakeFHIRRepositoryMock() - - h := FHIR.NewFHIRStoreImpl(d) input := domain.FHIRCompositionInput{} @@ -972,7 +961,7 @@ func TestStoreImpl_CreateFHIRComposition(t *testing.T) { { name: "Happy case", args: args{ - ctx: ctx, + ctx: context.Background(), input: input, }, wantErr: false, @@ -980,7 +969,7 @@ func TestStoreImpl_CreateFHIRComposition(t *testing.T) { { name: "Sad case", args: args{ - ctx: ctx, + ctx: context.Background(), input: input, }, wantErr: true, @@ -988,6 +977,10 @@ func TestStoreImpl_CreateFHIRComposition(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + var d = fakeDataset.NewFakeFHIRRepositoryMock() + + h := FHIR.NewFHIRStoreImpl(d) + if tt.name == "Sad case" { d.MockCreateFHIRResourceFn = func(resourceType string, payload map[string]interface{}) ([]byte, error) { return nil, fmt.Errorf("an error occurred") @@ -1004,10 +997,6 @@ func TestStoreImpl_CreateFHIRComposition(t *testing.T) { } func TestStoreImpl_UpdateFHIRComposition(t *testing.T) { - ctx := context.Background() - var d = fakeDataset.NewFakeFHIRRepositoryMock() - - h := FHIR.NewFHIRStoreImpl(d) UUID := uuid.New().String() @@ -1028,7 +1017,7 @@ func TestStoreImpl_UpdateFHIRComposition(t *testing.T) { { name: "Happy case", args: args{ - ctx: ctx, + ctx: context.Background(), input: input, }, wantErr: false, @@ -1036,7 +1025,7 @@ func TestStoreImpl_UpdateFHIRComposition(t *testing.T) { { name: "Sad case", args: args{ - ctx: ctx, + ctx: context.Background(), input: input, }, wantErr: true, @@ -1044,6 +1033,10 @@ func TestStoreImpl_UpdateFHIRComposition(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + var d = fakeDataset.NewFakeFHIRRepositoryMock() + + h := FHIR.NewFHIRStoreImpl(d) + if tt.name == "Sad case" { d.MockUpdateFHIRResourceFn = func(resourceType, fhirResourceID string, payload map[string]interface{}) ([]byte, error) { return nil, fmt.Errorf("an error occurred") @@ -1059,10 +1052,6 @@ func TestStoreImpl_UpdateFHIRComposition(t *testing.T) { } func TestStoreImpl_DeleteFHIRComposition(t *testing.T) { - ctx := context.Background() - var d = fakeDataset.NewFakeFHIRRepositoryMock() - - h := FHIR.NewFHIRStoreImpl(d) id := ksuid.New().String() @@ -1079,7 +1068,7 @@ func TestStoreImpl_DeleteFHIRComposition(t *testing.T) { { name: "Happy case", args: args{ - ctx: ctx, + ctx: context.Background(), id: id, }, wantErr: false, @@ -1088,7 +1077,7 @@ func TestStoreImpl_DeleteFHIRComposition(t *testing.T) { { name: "Sad case", args: args{ - ctx: ctx, + ctx: context.Background(), id: id, }, wantErr: true, @@ -1097,6 +1086,10 @@ func TestStoreImpl_DeleteFHIRComposition(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + var d = fakeDataset.NewFakeFHIRRepositoryMock() + + h := FHIR.NewFHIRStoreImpl(d) + if tt.name == "Sad case" { d.MockDeleteFHIRResourceFn = func(resourceType, fhirResourceID string) ([]byte, error) { return nil, fmt.Errorf("an error occurred") diff --git a/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhirdataset/dataset.go b/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhirdataset/dataset.go index 1551ede2..a9e517af 100644 --- a/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhirdataset/dataset.go +++ b/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhirdataset/dataset.go @@ -30,13 +30,16 @@ type FHIRRepository interface { DeleteFHIRResource(resourceType, fhirResourceID string) ([]byte, error) PatchFHIRResource(resourceType, fhirResourceID string, payload []map[string]interface{}) ([]byte, error) UpdateFHIRResource(resourceType, fhirResourceID string, payload map[string]interface{}) ([]byte, error) + GetFHIRPatientAllData(fhirResourceID string) ([]byte, error) FHIRRestURL() string GetFHIRResource(resourceType, fhirResourceID string) ([]byte, error) POSTRequest(resourceName string, path string, params url.Values, body io.Reader) ([]byte, error) FHIRHeaders() (http.Header, error) + CreateDataset() (*healthcare.Operation, error) GetDataset() (*healthcare.Dataset, error) + GetFHIRStore() (*healthcare.FhirStore, error) CreateFHIRStore() (*healthcare.FhirStore, error) } @@ -49,7 +52,7 @@ type Repository struct { } // NewFHIRRepository initializes a FHIR repository -func NewFHIRRepository(ctx context.Context) FHIRRepository { +func NewFHIRRepository(ctx context.Context) *Repository { project := serverutils.MustGetEnvVar(serverutils.GoogleCloudProjectIDEnvVarName) _ = serverutils.MustGetEnvVar("CLOUD_HEALTH_PUBSUB_TOPIC") dataset := serverutils.MustGetEnvVar("CLOUD_HEALTH_DATASET_ID") diff --git a/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhirdataset/dataset_unit_test.go b/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhirdataset/dataset_unit_test.go deleted file mode 100644 index f20911e5..00000000 --- a/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhirdataset/dataset_unit_test.go +++ /dev/null @@ -1 +0,0 @@ -package fhirdataset_test diff --git a/pkg/clinical/infrastructure/infrastructure.go b/pkg/clinical/infrastructure/infrastructure.go index 1f166462..ee195d15 100644 --- a/pkg/clinical/infrastructure/infrastructure.go +++ b/pkg/clinical/infrastructure/infrastructure.go @@ -1,17 +1,34 @@ package infrastructure import ( + "context" + "io" + "net/http" + "net/url" + "github.com/savannahghi/clinical/pkg/clinical/application/common" "github.com/savannahghi/clinical/pkg/clinical/application/extensions" "github.com/savannahghi/clinical/pkg/clinical/infrastructure/services/mycarehub" - "github.com/savannahghi/clinical/pkg/clinical/infrastructure/services/openconceptlab" "github.com/savannahghi/clinical/pkg/clinical/repository" ) +// ServiceOCL ... +type ServiceOCL interface { + MakeRequest(method string, path string, params url.Values, body io.Reader) (*http.Response, error) + ListConcepts( + ctx context.Context, org string, source string, verbose bool, q *string, + sortAsc *string, sortDesc *string, conceptClass *string, dataType *string, + locale *string, includeRetired *bool, + includeMappings *bool, includeInverseMappings *bool) ([]map[string]interface{}, error) + GetConcept( + ctx context.Context, org string, source string, concept string, + includeMappings bool, includeInverseMappings bool) (map[string]interface{}, error) +} + // Infrastructure ... type Infrastructure struct { FHIR repository.FHIR - OpenConceptLab openconceptlab.ServiceOCL + OpenConceptLab ServiceOCL BaseExtension extensions.BaseExtension MyCareHub mycarehub.IServiceMyCareHub } @@ -20,7 +37,7 @@ type Infrastructure struct { func NewInfrastructureInteractor( ext extensions.BaseExtension, fhir repository.FHIR, - openconceptlab openconceptlab.ServiceOCL, + openconceptlab ServiceOCL, ) Infrastructure { myCareHubClient := common.NewInterServiceClient("mycarehub", ext) mycarehub := mycarehub.NewServiceMyCareHub(myCareHubClient, ext) diff --git a/pkg/clinical/infrastructure/services/openconceptlab/service.go b/pkg/clinical/infrastructure/services/openconceptlab/service.go index bd6acbe5..23d4736f 100644 --- a/pkg/clinical/infrastructure/services/openconceptlab/service.go +++ b/pkg/clinical/infrastructure/services/openconceptlab/service.go @@ -23,19 +23,6 @@ const ( OCLAPITimeoutSeconds = 30 ) -// ServiceOCL ... -type ServiceOCL interface { - MakeRequest(method string, path string, params url.Values, body io.Reader) (*http.Response, error) - ListConcepts( - ctx context.Context, org string, source string, verbose bool, q *string, - sortAsc *string, sortDesc *string, conceptClass *string, dataType *string, - locale *string, includeRetired *bool, - includeMappings *bool, includeInverseMappings *bool) ([]map[string]interface{}, error) - GetConcept( - ctx context.Context, org string, source string, concept string, - includeMappings bool, includeInverseMappings bool) (map[string]interface{}, error) -} - // NewServiceOCL creates a new open conceptlab Service func NewServiceOCL() *Service { baseURL := serverutils.MustGetEnvVar(OCLAPIURLEnvVarName) diff --git a/pkg/clinical/infrastructure/services/pubsub/service.go b/pkg/clinical/infrastructure/services/pubsub/service.go index 08384b00..dcb467bf 100644 --- a/pkg/clinical/infrastructure/services/pubsub/service.go +++ b/pkg/clinical/infrastructure/services/pubsub/service.go @@ -8,8 +8,8 @@ import ( "cloud.google.com/go/pubsub" "github.com/savannahghi/clinical/pkg/clinical/application/common" "github.com/savannahghi/clinical/pkg/clinical/application/extensions" + "github.com/savannahghi/clinical/pkg/clinical/domain" "github.com/savannahghi/clinical/pkg/clinical/infrastructure" - "github.com/savannahghi/clinical/pkg/clinical/usecases/clinical" "github.com/savannahghi/pubsubtools" "github.com/savannahghi/serverutils" ) @@ -54,12 +54,23 @@ type ServicePubsub interface { ) } +// Clinical represents all the patient business logic +type Clinical interface { + FindOrganizationByID(ctx context.Context, organizationID string) (*domain.FHIROrganizationRelayPayload, error) + RegisterPatient(ctx context.Context, input domain.SimplePatientRegistrationInput) (*domain.PatientPayload, error) + CreateFHIRObservation(ctx context.Context, input domain.FHIRObservationInput) (*domain.FHIRObservationRelayPayload, error) + CreateFHIRAllergyIntolerance(ctx context.Context, input domain.FHIRAllergyIntoleranceInput) (*domain.FHIRAllergyIntoleranceRelayPayload, error) + FindPatientByID(ctx context.Context, id string) (*domain.PatientPayload, error) + CreateFHIRMedicationStatement(ctx context.Context, input domain.FHIRMedicationStatementInput) (*domain.FHIRMedicationStatementRelayPayload, error) + CreateFHIROrganization(ctx context.Context, input domain.FHIROrganizationInput) (*domain.FHIROrganizationRelayPayload, error) +} + // ServicePubSubMessaging is used to send and receive pubsub notifications type ServicePubSubMessaging struct { client *pubsub.Client baseExt extensions.BaseExtension infra infrastructure.Infrastructure - clinical clinical.UseCasesClinical + clinical Clinical } // NewServicePubSubMessaging returns a new instance of pubsub @@ -68,7 +79,7 @@ func NewServicePubSubMessaging( client *pubsub.Client, baseExt extensions.BaseExtension, infra infrastructure.Infrastructure, - clinical clinical.UseCasesClinical, + clinical Clinical, ) (*ServicePubSubMessaging, error) { s := &ServicePubSubMessaging{ client: client, diff --git a/pkg/clinical/infrastructure/services/pubsub/subscriber.go b/pkg/clinical/infrastructure/services/pubsub/subscriber.go index 250f777c..31e66960 100644 --- a/pkg/clinical/infrastructure/services/pubsub/subscriber.go +++ b/pkg/clinical/infrastructure/services/pubsub/subscriber.go @@ -4,8 +4,10 @@ import ( "context" "encoding/json" "fmt" + "io" "log" "net/http" + "net/url" "strings" "time" @@ -14,7 +16,6 @@ import ( "github.com/savannahghi/clinical/pkg/clinical/application/common" "github.com/savannahghi/clinical/pkg/clinical/application/dto" "github.com/savannahghi/clinical/pkg/clinical/domain" - "github.com/savannahghi/clinical/pkg/clinical/infrastructure/services/openconceptlab" "github.com/savannahghi/errorcodeutil" "github.com/savannahghi/pubsubtools" "github.com/savannahghi/scalarutils" @@ -27,6 +28,19 @@ var ( unknownConceptID = "1067" ) +// ServiceOCL ... +type ServiceOCL interface { + MakeRequest(method string, path string, params url.Values, body io.Reader) (*http.Response, error) + ListConcepts( + ctx context.Context, org string, source string, verbose bool, q *string, + sortAsc *string, sortDesc *string, conceptClass *string, dataType *string, + locale *string, includeRetired *bool, + includeMappings *bool, includeInverseMappings *bool) ([]map[string]interface{}, error) + GetConcept( + ctx context.Context, org string, source string, concept string, + includeMappings bool, includeInverseMappings bool) (map[string]interface{}, error) +} + // ReceivePubSubPushMessages receives and processes a pubsub message func (ps ServicePubSubMessaging) ReceivePubSubPushMessages( c *gin.Context, @@ -610,7 +624,7 @@ func (ps ServicePubSubMessaging) ComposeAllergyIntoleranceInput(ctx context.Cont return allergy, nil } -func getCIELConcept(ctx context.Context, ocl openconceptlab.ServiceOCL, conceptID string) (*domain.Concept, error) { +func getCIELConcept(ctx context.Context, ocl ServiceOCL, conceptID string) (*domain.Concept, error) { response, err := ocl.GetConcept( ctx, "CIEL", diff --git a/pkg/clinical/presentation/interactor/interactor.go b/pkg/clinical/presentation/interactor/interactor.go index 3df81fed..096d3c58 100644 --- a/pkg/clinical/presentation/interactor/interactor.go +++ b/pkg/clinical/presentation/interactor/interactor.go @@ -1,28 +1,62 @@ package interactor import ( + "context" + "io" + "net/http" + "net/url" + + "github.com/savannahghi/clinical/pkg/clinical/domain" "github.com/savannahghi/clinical/pkg/clinical/infrastructure" "github.com/savannahghi/clinical/pkg/clinical/usecases/clinical" "github.com/savannahghi/clinical/pkg/clinical/usecases/ocl" ) +// UseCasesClinical represents all the patient business logic +type Clinical interface { + FindOrganizationByID(ctx context.Context, organizationID string) (*domain.FHIROrganizationRelayPayload, error) + RegisterPatient(ctx context.Context, input domain.SimplePatientRegistrationInput) (*domain.PatientPayload, error) + CreateFHIRObservation(ctx context.Context, input domain.FHIRObservationInput) (*domain.FHIRObservationRelayPayload, error) + CreateFHIRAllergyIntolerance(ctx context.Context, input domain.FHIRAllergyIntoleranceInput) (*domain.FHIRAllergyIntoleranceRelayPayload, error) + FindPatientByID(ctx context.Context, id string) (*domain.PatientPayload, error) + CreateFHIRMedicationStatement(ctx context.Context, input domain.FHIRMedicationStatementInput) (*domain.FHIRMedicationStatementRelayPayload, error) + + CreateFHIROrganization(ctx context.Context, input domain.FHIROrganizationInput) (*domain.FHIROrganizationRelayPayload, error) + PatientTimeline(ctx context.Context, patientID string, count int) ([]map[string]interface{}, error) + PatientHealthTimeline(ctx context.Context, input domain.HealthTimelineInput) (*domain.HealthTimeline, error) + GetMedicalData(ctx context.Context, patientID string) (*domain.MedicalData, error) +} + +// OCLUsecase represents all the Open Concept Lab business logic +type OCLUsecase interface { + MakeRequest(method string, path string, params url.Values, body io.Reader) (*http.Response, error) + ListConcepts( + ctx context.Context, org string, source string, verbose bool, q *string, + sortAsc *string, sortDesc *string, conceptClass *string, dataType *string, + locale *string, includeRetired *bool, + includeMappings *bool, includeInverseMappings *bool) ([]map[string]interface{}, error) + GetConcept( + ctx context.Context, org string, source string, concept string, + includeMappings bool, includeInverseMappings bool) (map[string]interface{}, error) +} + // Usecases is an interface that combines of all usescases type Usecases interface { - clinical.UseCasesClinical - ocl.UseCasesOCL + Clinical + OCLUsecase } // Interactor is an implementation of the usecases interface type Interactor struct { - clinical.UseCasesClinical + Clinical infrastructure.Infrastructure - ocl.UseCasesOCL + OCLUsecase } // NewUsecasesInteractor initializes a new usecases interactor func NewUsecasesInteractor( infrastructure infrastructure.Infrastructure, -) Usecases { +) *Interactor { clinical := clinical.NewUseCasesClinicalImpl(infrastructure) ocl := ocl.NewUseCasesOCLImpl(infrastructure) diff --git a/pkg/clinical/presentation/rest/handlers.go b/pkg/clinical/presentation/rest/handlers.go index 3d5b7ac7..234de5e4 100644 --- a/pkg/clinical/presentation/rest/handlers.go +++ b/pkg/clinical/presentation/rest/handlers.go @@ -14,6 +14,6 @@ type PresentationHandlersImpl struct { } // NewPresentationHandlers initializes a new rest handlers usecase -func NewPresentationHandlers(usecases usecases.Interactor) PresentationHandlers { +func NewPresentationHandlers(usecases usecases.Interactor) *PresentationHandlersImpl { return &PresentationHandlersImpl{usecases: usecases} } diff --git a/pkg/clinical/repository/repository.go b/pkg/clinical/repository/repository.go index c065c98b..7a9ad23e 100644 --- a/pkg/clinical/repository/repository.go +++ b/pkg/clinical/repository/repository.go @@ -2,7 +2,6 @@ package repository import ( "context" - "net/http" "net/url" "github.com/savannahghi/clinical/pkg/clinical/domain" @@ -10,61 +9,90 @@ 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) + FHIROrganization + FHIRPatient + FHIREpisodeOfCare + FHIRObservation + FHIRAllergyIntolerance + FHIRServiceRequest + FHIRMedicationRequest + FHIRCondition + FHIREncounter + FHIRComposition + FHIRMedicationStatement + FHIRMedication +} + +type FHIROrganization interface { CreateFHIROrganization(ctx context.Context, input domain.FHIROrganizationInput) (*domain.FHIROrganizationRelayPayload, error) SearchFHIROrganization(ctx context.Context, params map[string]interface{}) (*domain.FHIROrganizationRelayConnection, error) FindOrganizationByID(ctx context.Context, organisationID string) (*domain.FHIROrganizationRelayPayload, error) +} + +type FHIRPatient interface { + GetFHIRPatient(ctx context.Context, id string) (*domain.FHIRPatientRelayPayload, error) + DeleteFHIRPatient(ctx context.Context, id string) (bool, error) + CreateFHIRPatient(ctx context.Context, input domain.FHIRPatientInput) (*domain.PatientPayload, error) + PatchFHIRPatient(ctx context.Context, id string, params []map[string]interface{}) (*domain.FHIRPatient, error) + SearchFHIRPatient(ctx context.Context, searchParams string) (*domain.PatientConnection, error) +} +type FHIREpisodeOfCare interface { + SearchFHIREpisodeOfCare(ctx context.Context, params map[string]interface{}) (*domain.FHIREpisodeOfCareRelayConnection, error) SearchEpisodesByParam(ctx context.Context, searchParams url.Values) ([]*domain.FHIREpisodeOfCare, error) - HasOpenEpisode( - ctx context.Context, - patient domain.FHIRPatient, - ) (bool, error) - OpenEpisodes( - ctx context.Context, patientReference string) ([]*domain.FHIREpisodeOfCare, error) - CreateFHIREncounter(ctx context.Context, input domain.FHIREncounterInput) (*domain.FHIREncounterRelayPayload, error) GetFHIREpisodeOfCare(ctx context.Context, id string) (*domain.FHIREpisodeOfCareRelayPayload, error) - Encounters(ctx context.Context, patientReference string, status *domain.EncounterStatusEnum) ([]*domain.FHIREncounter, error) - SearchFHIREpisodeOfCare(ctx context.Context, params map[string]interface{}) (*domain.FHIREpisodeOfCareRelayConnection, error) - StartEncounter(ctx context.Context, episodeID string) (string, error) - SearchEpisodeEncounter( - ctx context.Context, - episodeReference string, - ) (*domain.FHIREncounterRelayConnection, error) - EndEncounter(ctx context.Context, encounterID string) (bool, error) + CreateEpisodeOfCare(ctx context.Context, episode domain.FHIREpisodeOfCare) (*domain.EpisodeOfCarePayload, error) + UpdateFHIREpisodeOfCare(ctx context.Context, fhirResourceID string, payload map[string]interface{}) (*domain.FHIREpisodeOfCare, error) + HasOpenEpisode(ctx context.Context, patient domain.FHIRPatient) (bool, error) + OpenEpisodes(ctx context.Context, patientReference string) ([]*domain.FHIREpisodeOfCare, error) EndEpisode(ctx context.Context, episodeID string) (bool, error) GetActiveEpisode(ctx context.Context, episodeID string) (*domain.FHIREpisodeOfCare, error) - SearchFHIRServiceRequest(ctx context.Context, params map[string]interface{}) (*domain.FHIRServiceRequestRelayConnection, error) - CreateFHIRServiceRequest(ctx context.Context, input domain.FHIRServiceRequestInput) (*domain.FHIRServiceRequestRelayPayload, error) +} +type FHIRObservation interface { + SearchFHIRObservation(ctx context.Context, params map[string]interface{}) (*domain.FHIRObservationRelayConnection, error) + CreateFHIRObservation(ctx context.Context, input domain.FHIRObservationInput) (*domain.FHIRObservationRelayPayload, error) + DeleteFHIRObservation(ctx context.Context, id string) (bool, error) +} +type FHIRAllergyIntolerance interface { SearchFHIRAllergyIntolerance(ctx context.Context, params map[string]interface{}) (*domain.FHIRAllergyIntoleranceRelayConnection, error) CreateFHIRAllergyIntolerance(ctx context.Context, input domain.FHIRAllergyIntoleranceInput) (*domain.FHIRAllergyIntoleranceRelayPayload, error) UpdateFHIRAllergyIntolerance(ctx context.Context, input domain.FHIRAllergyIntoleranceInput) (*domain.FHIRAllergyIntoleranceRelayPayload, error) - SearchFHIRComposition(ctx context.Context, params map[string]interface{}) (*domain.FHIRCompositionRelayConnection, error) - CreateFHIRComposition(ctx context.Context, input domain.FHIRCompositionInput) (*domain.FHIRCompositionRelayPayload, error) - UpdateFHIRComposition(ctx context.Context, input domain.FHIRCompositionInput) (*domain.FHIRCompositionRelayPayload, error) - DeleteFHIRComposition(ctx context.Context, id string) (bool, error) - UpdateFHIRCondition(ctx context.Context, input domain.FHIRConditionInput) (*domain.FHIRConditionRelayPayload, error) - GetFHIREncounter(ctx context.Context, id string) (*domain.FHIREncounterRelayPayload, error) - SearchFHIREncounter(ctx context.Context, params map[string]interface{}) (*domain.FHIREncounterRelayConnection, error) +} +type FHIRServiceRequest interface { + SearchFHIRServiceRequest(ctx context.Context, params map[string]interface{}) (*domain.FHIRServiceRequestRelayConnection, error) + CreateFHIRServiceRequest(ctx context.Context, input domain.FHIRServiceRequestInput) (*domain.FHIRServiceRequestRelayPayload, error) + DeleteFHIRServiceRequest(ctx context.Context, id string) (bool, error) +} +type FHIRMedicationRequest interface { SearchFHIRMedicationRequest(ctx context.Context, params map[string]interface{}) (*domain.FHIRMedicationRequestRelayConnection, error) CreateFHIRMedicationRequest(ctx context.Context, input domain.FHIRMedicationRequestInput) (*domain.FHIRMedicationRequestRelayPayload, error) UpdateFHIRMedicationRequest(ctx context.Context, input domain.FHIRMedicationRequestInput) (*domain.FHIRMedicationRequestRelayPayload, error) DeleteFHIRMedicationRequest(ctx context.Context, id string) (bool, error) - SearchFHIRObservation(ctx context.Context, params map[string]interface{}) (*domain.FHIRObservationRelayConnection, error) - CreateFHIRObservation(ctx context.Context, input domain.FHIRObservationInput) (*domain.FHIRObservationRelayPayload, error) - DeleteFHIRObservation(ctx context.Context, id string) (bool, error) - GetFHIRPatient(ctx context.Context, id string) (*domain.FHIRPatientRelayPayload, error) - DeleteFHIRPatient(ctx context.Context, id string) (bool, error) - DeleteFHIRServiceRequest(ctx context.Context, id string) (bool, error) - DeleteFHIRResourceType(results []map[string]string) error +} +type FHIRCondition interface { + SearchFHIRCondition(ctx context.Context, params map[string]interface{}) (*domain.FHIRConditionRelayConnection, error) + CreateFHIRCondition(ctx context.Context, input domain.FHIRConditionInput) (*domain.FHIRConditionRelayPayload, error) + UpdateFHIRCondition(ctx context.Context, input domain.FHIRConditionInput) (*domain.FHIRConditionRelayPayload, error) +} +type FHIREncounter interface { + CreateFHIREncounter(ctx context.Context, input domain.FHIREncounterInput) (*domain.FHIREncounterRelayPayload, error) + Encounters(ctx context.Context, patientReference string, status *domain.EncounterStatusEnum) ([]*domain.FHIREncounter, error) + StartEncounter(ctx context.Context, episodeID string) (string, error) + SearchEpisodeEncounter(ctx context.Context, episodeReference string) (*domain.FHIREncounterRelayConnection, error) + EndEncounter(ctx context.Context, encounterID string) (bool, error) + GetFHIREncounter(ctx context.Context, id string) (*domain.FHIREncounterRelayPayload, error) + SearchFHIREncounter(ctx context.Context, params map[string]interface{}) (*domain.FHIREncounterRelayConnection, error) +} +type FHIRComposition interface { + SearchFHIRComposition(ctx context.Context, params map[string]interface{}) (*domain.FHIRCompositionRelayConnection, error) + CreateFHIRComposition(ctx context.Context, input domain.FHIRCompositionInput) (*domain.FHIRCompositionRelayPayload, error) + UpdateFHIRComposition(ctx context.Context, input domain.FHIRCompositionInput) (*domain.FHIRCompositionRelayPayload, error) + DeleteFHIRComposition(ctx context.Context, id string) (bool, error) +} +type FHIRMedicationStatement 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) - SearchFHIRPatient(ctx context.Context, searchParams string) (*domain.PatientConnection, error) +} - FHIRHeaders() (http.Header, error) +type FHIRMedication interface { + CreateFHIRMedication(ctx context.Context, input domain.FHIRMedicationInput) (*domain.FHIRMedicationRelayPayload, error) } diff --git a/pkg/clinical/usecases/clinical/patient.go b/pkg/clinical/usecases/clinical/patient.go index 33b1b344..26fbd358 100644 --- a/pkg/clinical/usecases/clinical/patient.go +++ b/pkg/clinical/usecases/clinical/patient.go @@ -29,28 +29,13 @@ const ( TwilioSMSNumberEnvVarName = "TWILIO_SMS_NUMBER" ) -// UseCasesClinical represents all the patient business logic -type UseCasesClinical interface { - FindOrganizationByID(ctx context.Context, organizationID string) (*domain.FHIROrganizationRelayPayload, error) - RegisterPatient(ctx context.Context, input domain.SimplePatientRegistrationInput) (*domain.PatientPayload, error) - CreateFHIRObservation(ctx context.Context, input domain.FHIRObservationInput) (*domain.FHIRObservationRelayPayload, error) - CreateFHIRAllergyIntolerance(ctx context.Context, input domain.FHIRAllergyIntoleranceInput) (*domain.FHIRAllergyIntoleranceRelayPayload, error) - FindPatientByID(ctx context.Context, id string) (*domain.PatientPayload, error) - CreateFHIRMedicationStatement(ctx context.Context, input domain.FHIRMedicationStatementInput) (*domain.FHIRMedicationStatementRelayPayload, error) - - CreateFHIROrganization(ctx context.Context, input domain.FHIROrganizationInput) (*domain.FHIROrganizationRelayPayload, error) - PatientTimeline(ctx context.Context, patientID string, count int) ([]map[string]interface{}, error) - PatientHealthTimeline(ctx context.Context, input domain.HealthTimelineInput) (*domain.HealthTimeline, error) - GetMedicalData(ctx context.Context, patientID string) (*domain.MedicalData, error) -} - // UseCasesClinicalImpl represents the patient usecase implementation type UseCasesClinicalImpl struct { infrastructure infrastructure.Infrastructure } // NewUseCasesClinicalImpl initializes new Clinical/Patient implementation -func NewUseCasesClinicalImpl(infra infrastructure.Infrastructure) UseCasesClinical { +func NewUseCasesClinicalImpl(infra infrastructure.Infrastructure) *UseCasesClinicalImpl { return &UseCasesClinicalImpl{ infrastructure: infra, } diff --git a/pkg/clinical/usecases/config_test.go b/pkg/clinical/usecases/config_test.go index d57d66f2..6349131a 100644 --- a/pkg/clinical/usecases/config_test.go +++ b/pkg/clinical/usecases/config_test.go @@ -14,7 +14,6 @@ import ( "github.com/savannahghi/clinical/pkg/clinical/infrastructure/services/openconceptlab" "github.com/savannahghi/clinical/pkg/clinical/presentation/interactor" "github.com/savannahghi/clinical/pkg/clinical/usecases" - "github.com/savannahghi/clinical/pkg/clinical/usecases/ocl" oclMock "github.com/savannahghi/clinical/pkg/clinical/usecases/ocl/mock" log "github.com/sirupsen/logrus" ) @@ -86,7 +85,7 @@ func InitializeTestInfrastructure(ctx context.Context) (infrastructure.Infrastru } func InitializeFakeTestlInteractor(ctx context.Context) (usecases.Interactor, error) { - var ocl ocl.UseCasesOCL = &fakeOCL + var ocl infrastructure.ServiceOCL = &fakeOCL var baseExt extensions.BaseExtension = &fakeBaseExtension infra := func() infrastructure.Infrastructure { return infrastructure.Infrastructure{ diff --git a/pkg/clinical/usecases/ocl/ocl.go b/pkg/clinical/usecases/ocl/ocl.go index 160ef1d4..9b2b0adf 100644 --- a/pkg/clinical/usecases/ocl/ocl.go +++ b/pkg/clinical/usecases/ocl/ocl.go @@ -9,19 +9,6 @@ import ( "github.com/savannahghi/clinical/pkg/clinical/infrastructure" ) -// UseCasesOCL represents all the Open Concept Lab business logic -type UseCasesOCL interface { - MakeRequest(method string, path string, params url.Values, body io.Reader) (*http.Response, error) - ListConcepts( - ctx context.Context, org string, source string, verbose bool, q *string, - sortAsc *string, sortDesc *string, conceptClass *string, dataType *string, - locale *string, includeRetired *bool, - includeMappings *bool, includeInverseMappings *bool) ([]map[string]interface{}, error) - GetConcept( - ctx context.Context, org string, source string, concept string, - includeMappings bool, includeInverseMappings bool) (map[string]interface{}, error) -} - // UseCasesOCLImpl represents the OCL usecase implementation type UseCasesOCLImpl struct { infrastructure infrastructure.Infrastructure diff --git a/pkg/clinical/usecases/usecases.go b/pkg/clinical/usecases/usecases.go index e0eac7f8..f5d09ecb 100644 --- a/pkg/clinical/usecases/usecases.go +++ b/pkg/clinical/usecases/usecases.go @@ -1,16 +1,49 @@ package usecases import ( + "context" + "io" + "net/http" + "net/url" + + "github.com/savannahghi/clinical/pkg/clinical/domain" "github.com/savannahghi/clinical/pkg/clinical/infrastructure" clinicalUsecase "github.com/savannahghi/clinical/pkg/clinical/usecases/clinical" "github.com/savannahghi/clinical/pkg/clinical/usecases/ocl" ) +// Clinical represents all the patient business logic +type Clinical interface { + FindOrganizationByID(ctx context.Context, organizationID string) (*domain.FHIROrganizationRelayPayload, error) + RegisterPatient(ctx context.Context, input domain.SimplePatientRegistrationInput) (*domain.PatientPayload, error) + CreateFHIRObservation(ctx context.Context, input domain.FHIRObservationInput) (*domain.FHIRObservationRelayPayload, error) + CreateFHIRAllergyIntolerance(ctx context.Context, input domain.FHIRAllergyIntoleranceInput) (*domain.FHIRAllergyIntoleranceRelayPayload, error) + FindPatientByID(ctx context.Context, id string) (*domain.PatientPayload, error) + CreateFHIRMedicationStatement(ctx context.Context, input domain.FHIRMedicationStatementInput) (*domain.FHIRMedicationStatementRelayPayload, error) + + CreateFHIROrganization(ctx context.Context, input domain.FHIROrganizationInput) (*domain.FHIROrganizationRelayPayload, error) + PatientHealthTimeline(ctx context.Context, input domain.HealthTimelineInput) (*domain.HealthTimeline, error) + GetMedicalData(ctx context.Context, patientID string) (*domain.MedicalData, error) +} + +// OCL represents all the Open Concept Lab business logic +type OCL interface { + MakeRequest(method string, path string, params url.Values, body io.Reader) (*http.Response, error) + ListConcepts( + ctx context.Context, org string, source string, verbose bool, q *string, + sortAsc *string, sortDesc *string, conceptClass *string, dataType *string, + locale *string, includeRetired *bool, + includeMappings *bool, includeInverseMappings *bool) ([]map[string]interface{}, error) + GetConcept( + ctx context.Context, org string, source string, concept string, + includeMappings bool, includeInverseMappings bool) (map[string]interface{}, error) +} + // Interactor is an implementation of the usecases interface type Interactor struct { infra infrastructure.Infrastructure - clinicalUsecase.UseCasesClinical - ocl.UseCasesOCL + Clinical + OCL } // NewUsecasesInteractor initializes a new usecases interactor