Skip to content

Commit

Permalink
ref: refactor ocl implementation
Browse files Browse the repository at this point in the history
Improve usecase implementation and add acceptance tests

Signed-off-by: Otieno Calvine <nyarangaotieno@gmail.com>
  • Loading branch information
NYARAS committed Oct 4, 2021
1 parent 751a69b commit f698f64
Show file tree
Hide file tree
Showing 10 changed files with 364 additions and 14 deletions.
4 changes: 4 additions & 0 deletions pkg/clinical/infrastructure/infrastructure.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/savannahghi/clinical/pkg/clinical/application/extensions"
"github.com/savannahghi/clinical/pkg/clinical/infrastructure/services/engagement"
"github.com/savannahghi/clinical/pkg/clinical/infrastructure/services/onboarding"
"github.com/savannahghi/clinical/pkg/clinical/infrastructure/services/openconceptlab"
"github.com/savannahghi/firebasetools"
)

Expand All @@ -19,6 +20,7 @@ type Infrastructure struct {
Engagement engagement.ServiceEngagement
FirestoreClient *firestore.Client
Onboarding onboarding.ServiceOnboarding
OpenConceptLab openconceptlab.ServiceOCL
}

// NewInfrastructureInteractor initializes a new Infrastructure
Expand All @@ -30,6 +32,7 @@ func NewInfrastructureInteractor() Infrastructure {
firestoreDB := NewDBService()
onboardingClient := common.NewInterServiceClient("onboarding", baseExtension)
onboarding := onboarding.NewServiceOnboardingImpl(onboardingClient, baseExtension)
openconceptlab := openconceptlab.NewServiceOCL()

fc := firebasetools.FirebaseClient{}
fa, err := fc.InitFirebase()
Expand All @@ -48,5 +51,6 @@ func NewInfrastructureInteractor() Infrastructure {
engagement,
fsc,
onboarding,
openconceptlab,
}
}
22 changes: 19 additions & 3 deletions pkg/clinical/infrastructure/services/openconceptlab/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,31 @@ const (
OCLAPITimeoutSeconds = 30
)

// NewService initializes a new OpenConceptLab service
func NewService() *Service {
// 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)
token := serverutils.MustGetEnvVar(OCLTokenEnvVarName)
header := fmt.Sprintf("Authorization: Token %s", token)
return &Service{

srv := &Service{
baseURL: baseURL,
header: header,
}
srv.enforcePreconditions()
return srv
}

// Service is an OpenConceptLab service
Expand Down
7 changes: 5 additions & 2 deletions pkg/clinical/presentation/graph/ocl.resolvers.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ package graph

import (
"context"
"fmt"
)

func (r *queryResolver) 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) {
panic(fmt.Errorf("not implemented"))
r.CheckUserTokenInContext(ctx)
r.CheckDependencies()
return r.usecases.ListConcepts(
ctx, org, source, verbose, q, sortAsc, sortDesc, conceptClass, dataType,
locale, includeRetired, includeMappings, includeInverseMappings)
}
11 changes: 9 additions & 2 deletions pkg/clinical/usecases/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/savannahghi/clinical/pkg/clinical/presentation/interactor"
"github.com/savannahghi/clinical/pkg/clinical/usecases"
usecaseMock "github.com/savannahghi/clinical/pkg/clinical/usecases/mock"
"github.com/savannahghi/clinical/pkg/clinical/usecases/ocl"
"github.com/savannahghi/converterandformatter"
"github.com/savannahghi/enumutils"
"github.com/savannahghi/firebasetools"
Expand All @@ -41,6 +42,7 @@ var (

testUsecaseInteractor interactor.Usecases
testInfrastructure infrastructure.Infrastructure
fakeOCL usecaseMock.OCLMock
)

func TestMain(m *testing.M) {
Expand Down Expand Up @@ -76,7 +78,7 @@ func TestMain(m *testing.M) {

testUsecaseInteractor = svc

fakeUsecaseIntr, err = InitializeFakeTestService(&fakePatient, &fakeFhir)
fakeUsecaseIntr, err = InitializeFakeTestService(&fakePatient, &fakeFhir, &fakeOCL)
if err != nil {
log.Printf("failed to initialize fake test service: %v", err)
}
Expand Down Expand Up @@ -143,13 +145,18 @@ func InitializeTestFirebaseClient(
return fsc, fbc
}

func InitializeFakeTestService(patient usecases.ClinicalUseCase, fhir usecases.FHIRUseCase) (
func InitializeFakeTestService(
patient usecases.ClinicalUseCase,
fhir usecases.FHIRUseCase,
ocl ocl.UseCases,
) (
usecases.Interactor,
error,
) {
itr := usecases.Interactor{
patient,
fhir,
ocl,
}
return itr, nil
}
Expand Down
6 changes: 3 additions & 3 deletions pkg/clinical/usecases/fhir.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const (
defaultTimeoutSeconds = 10
)

// FHIRUseCase ...
// FHIRUseCase represents all the FHIR business logic
type FHIRUseCase interface {
CreateEpisodeOfCare(ctx context.Context, episode domain.FHIREpisodeOfCare) (*domain.EpisodeOfCarePayload, error)
CreateFHIRCondition(ctx context.Context, input domain.FHIRConditionInput) (*domain.FHIRConditionRelayPayload, error)
Expand Down Expand Up @@ -116,12 +116,12 @@ type FHIRUseCase interface {
DeleteFHIRServiceRequest(ctx context.Context, id string) (bool, error)
}

// FHIRUseCaseImpl ...
// FHIRUseCaseImpl represents the FHIR usecase implementation
type FHIRUseCaseImpl struct {
infrastructure infrastructure.Infrastructure
}

// NewFHIRUseCaseImpl ...
// NewFHIRUseCaseImpl initializes the new FHIR implementation
func NewFHIRUseCaseImpl(infra infrastructure.Infrastructure) FHIRUseCase {
return &FHIRUseCaseImpl{
infrastructure: infra,
Expand Down
84 changes: 84 additions & 0 deletions pkg/clinical/usecases/mock/ocl_mock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package mock

import (
"context"
"io"
"net/http"
"net/url"
)

// OCLMock instantiates all the mock functions
type OCLMock struct {
MakeRequestFn func(method string, path string, params url.Values, body io.Reader) (*http.Response, error)
ListConceptsFn func(
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)
GetConceptFn func(
ctx context.Context, org string, source string, concept string,
includeMappings bool, includeInverseMappings bool) (map[string]interface{}, error)
}

// NewOCLMock is a new instance of OCLMock
func NewOCLMock() *OCLMock {
return &OCLMock{
MakeRequestFn: func(method string, path string, params url.Values, body io.Reader) (*http.Response, error) {
return &http.Response{}, nil
},
ListConceptsFn: func(
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) {
return nil, nil
},
GetConceptFn: func(
ctx context.Context, org string, source string, concept string,
includeMappings bool, includeInverseMappings bool) (map[string]interface{}, error) {
return nil, nil
},
}
}

//MakeRequest is the MakeRequest mock
func (ocl *OCLMock) MakeRequest(method string, path string, params url.Values, body io.Reader) (*http.Response, error) {
return ocl.MakeRequestFn(method, path, params, body)
}

//ListConcepts is the ListConcepts mock
func (ocl *OCLMock) 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) {
return ocl.ListConceptsFn(
ctx,
org,
source,
verbose,
q,
sortAsc,
sortDesc,
conceptClass,
dataType,
locale,
includeRetired,
includeMappings,
includeInverseMappings,
)
}

// GetConcept is the GetConcept mock
func (ocl *OCLMock) GetConcept(
ctx context.Context, org string, source string, concept string,
includeMappings bool, includeInverseMappings bool) (map[string]interface{}, error) {
return ocl.GetConceptFn(
ctx,
org,
source,
concept,
includeMappings,
includeInverseMappings,
)
}
76 changes: 75 additions & 1 deletion pkg/clinical/usecases/ocl/ocl.go
Original file line number Diff line number Diff line change
@@ -1 +1,75 @@
package usecases
package ocl

import (
"context"
"io"
"net/http"
"net/url"

"github.com/savannahghi/clinical/pkg/clinical/infrastructure"
)

// UseCases represents all the Open Concept Lab business logic
type UseCases 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)
}

// UseCasesImpl represents the OCL usecase implementation
type UseCasesImpl struct {
infrastructure infrastructure.Infrastructure
}

// NewUseCasesImpl initializes Open Concept Lab implementation
func NewUseCasesImpl(infrastructure infrastructure.Infrastructure) *UseCasesImpl {
return &UseCasesImpl{infrastructure: infrastructure}
}

// MakeRequest composes an authenticated OCL request that has the correct content type
func (ocl *UseCasesImpl) MakeRequest(method string, path string, params url.Values, body io.Reader) (*http.Response, error) {
return ocl.infrastructure.OpenConceptLab.MakeRequest(method, path, params, body)
}

// ListConcepts retrieves a single concept from OpenConceptLab.
func (ocl *UseCasesImpl) 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) {
return ocl.infrastructure.OpenConceptLab.ListConcepts(
ctx,
org,
source,
verbose,
q,
sortAsc,
sortDesc,
conceptClass,
dataType,
locale,
includeRetired,
includeMappings,
includeInverseMappings,
)
}

// GetConcept searches for matching concepts on OpenConceptLab
func (ocl *UseCasesImpl) GetConcept(
ctx context.Context, org string, source string, concept string,
includeMappings bool, includeInverseMappings bool) (map[string]interface{}, error) {
return ocl.infrastructure.OpenConceptLab.GetConcept(
ctx,
org,
source,
concept,
includeMappings,
includeInverseMappings,
)
}
6 changes: 3 additions & 3 deletions pkg/clinical/usecases/patient.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const (
sendSMSEndpoint = "internal/send_sms"
)

// ClinicalUseCase ...
// ClinicalUseCase represents all the patient business logic
type ClinicalUseCase interface {
ProblemSummary(ctx context.Context, patientID string) ([]string, error)
VisitSummary(ctx context.Context, encounterID string, count int) (map[string]interface{}, error)
Expand All @@ -53,13 +53,13 @@ type ClinicalUseCase interface {
FindPatientsByMSISDN(ctx context.Context, msisdn string) (*domain.PatientConnection, error)
}

// ClinicalUseCaseImpl ...
// ClinicalUseCaseImpl represents the patient usecase implementation
type ClinicalUseCaseImpl struct {
infrastructure infrastructure.Infrastructure
fhir FHIRUseCase
}

// NewClinicalUseCaseImpl ...
// NewClinicalUseCaseImpl initializes new Clinical/Patient implementation
func NewClinicalUseCaseImpl(infra infrastructure.Infrastructure, fhir FHIRUseCase) ClinicalUseCase {
return &ClinicalUseCaseImpl{
infrastructure: infra,
Expand Down
4 changes: 4 additions & 0 deletions pkg/clinical/usecases/usecases.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package usecases

import (
"github.com/savannahghi/clinical/pkg/clinical/infrastructure"
"github.com/savannahghi/clinical/pkg/clinical/usecases/ocl"
)

// Interactor is an implementation of the usecases interface
type Interactor struct {
ClinicalUseCase
FHIRUseCase
ocl.UseCases
}

// NewUsecasesInteractor initializes a new usecases interactor
Expand All @@ -16,10 +18,12 @@ func NewUsecasesInteractor(
) Interactor {
fhir := NewFHIRUseCaseImpl(infrastructure)
clinical := NewClinicalUseCaseImpl(infrastructure, fhir)
ocl := ocl.NewUseCasesImpl(infrastructure)

impl := Interactor{
clinical,
fhir,
ocl,
}

return impl
Expand Down
Loading

0 comments on commit f698f64

Please sign in to comment.