Skip to content

Commit

Permalink
feat: register clinical tenant via pubsub
Browse files Browse the repository at this point in the history
- used to register the program created in MCH as a tenant in this service

Signed-off-by: Kathurima Kimathi <kathurimakimathi415@gmail.com>
  • Loading branch information
KathurimaKimathi committed Apr 4, 2023
1 parent fcee851 commit 9204b6a
Show file tree
Hide file tree
Showing 12 changed files with 315 additions and 3 deletions.
3 changes: 3 additions & 0 deletions pkg/clinical/application/common/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ const (
// OrganizationTopicName is the topic where organization(facility) details are published to
OrganizationTopicName = "organization.create"

// TenantTopicName is the topic where program is registered in clinical as a tenant
TenantTopicName = "mycarehub.tenant.create"

// MedicalDataCount is the count of medical records
MedicalDataCount = "3"

Expand Down
1 change: 1 addition & 0 deletions pkg/clinical/application/dto/enums.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ type OrganizationIdentifierType string
const (
SladeCode OrganizationIdentifierType = "SladeCode"
MFLCode OrganizationIdentifierType = "MFLCode"
ProgramID OrganizationIdentifierType = "MCHProgram"
Other OrganizationIdentifierType = "Other"
)

Expand Down
2 changes: 2 additions & 0 deletions pkg/clinical/infrastructure/infrastructure.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ type IServiceMyCareHub interface {
fhirID string,
facilityID string,
) error

UpdateProgramFHIRTenantID(ctx context.Context, programID string, tenantID string) error
}

// Infrastructure ...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ type FakeMyCareHubService struct {
fhirID string,
facilityID string,
) error

MockUpdateProgramFHIRTenantIDFn func(ctx context.Context, programID string, tenantID string) error
}

// NewFakeMyCareHubServiceMock initializes a new instance of mycarehub mock
Expand Down Expand Up @@ -62,6 +64,9 @@ func NewFakeMyCareHubServiceMock() *FakeMyCareHubService {
) error {
return nil
},
MockUpdateProgramFHIRTenantIDFn: func(ctx context.Context, programID, tenantID string) error {
return nil
},
}
}

Expand Down Expand Up @@ -90,3 +95,8 @@ func (s *FakeMyCareHubService) AddFHIRIDToFacility(
) error {
return s.MockAddFHIRIDToFacilityFn(ctx, fhirID, facilityID)
}

// UpdateProgramFHIRTenantID amocks the update of program mycarehub's program fhir tenant id
func (s *FakeMyCareHubService) UpdateProgramFHIRTenantID(ctx context.Context, programID string, tenantID string) error {
return s.MockUpdateProgramFHIRTenantIDFn(ctx, programID, tenantID)
}
33 changes: 30 additions & 3 deletions pkg/clinical/infrastructure/services/mycarehub/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ import (
)

const (
getUserProfile = "internal/user-profile/%s"
addFHIRIDToProfile = "internal/add-fhir-id"
addFHIRIDToFacility = "internal/facilities"
getUserProfile = "internal/user-profile/%s"
addFHIRIDToProfile = "internal/add-fhir-id"
addFHIRIDToFacility = "internal/facilities"
updateProgramTenantID = "internal/program"
)

// ServiceMyCareHubImpl represents mycarehub usecases
Expand Down Expand Up @@ -149,3 +150,29 @@ func (s ServiceMyCareHubImpl) AddFHIRIDToFacility(

return nil
}

// UpdateProgramFHIRTenantID makes an isc call and updates mycarehub's tenant id in a certain program
func (s ServiceMyCareHubImpl) UpdateProgramFHIRTenantID(ctx context.Context, programID string, tenantID string) error {
type requestPayload struct {
ProgramID string `json:"programID"`
FHIRTenantID string `json:"fhirTenantID"`
}

resp, err := s.MyCareHubClient.MakeRequest(
ctx,
http.MethodPost,
updateProgramTenantID,
&requestPayload{ProgramID: programID, FHIRTenantID: tenantID},
)
if err != nil {
return fmt.Errorf("failed to make a request to mycarehub service: %w", err)
}

defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return fmt.Errorf("failed to update program tenant ID : %w, with status code %v", err, resp.StatusCode)
}

return nil
}
94 changes: 94 additions & 0 deletions pkg/clinical/infrastructure/services/mycarehub/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,3 +328,97 @@ func TestServiceMyCareHubImpl_AddFHIRIDToFacility(t *testing.T) {
})
}
}

func TestServiceMyCareHubImpl_UpdateProgramFHIRTenantID(t *testing.T) {
type args struct {
ctx context.Context
programID string
tenantID string
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "Happy case: update program tenant id",
args: args{
ctx: context.Background(),
programID: gofakeit.UUID(),
tenantID: gofakeit.UUID(),
},
wantErr: false,
},
{
name: "Sad Case: failed to make request",
args: args{
ctx: context.Background(),
programID: gofakeit.UUID(),
tenantID: gofakeit.UUID(),
},
wantErr: true,
},
{
name: "Sad case: unable to update program tenant id",
args: args{
ctx: context.Background(),
programID: gofakeit.UUID(),
tenantID: gofakeit.UUID(),
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fakeISC := extMock.NewFakeISCExtensionMock()
s := ServiceMyCareHubImpl{
MyCareHubClient: fakeISC,
}

if tt.name == "Happy case: update program tenant id" {
fakeISC.MockMakeRequestFn = func(ctx context.Context, method, path string, body interface{}) (*http.Response, error) {
requestBody := struct {
ProgramID string `json:"programID"`
FHIRTenantID string `json:"fhirTenantID"`
}{
ProgramID: tt.args.programID,
FHIRTenantID: tt.args.tenantID,
}

jsonBody, err := json.Marshal(requestBody)
if err != nil {
return nil, err
}
return &http.Response{
Status: "200",
StatusCode: 200,
Body: io.NopCloser(bytes.NewReader(jsonBody)),
}, nil
}
}
if tt.name == "Sad Case: failed to make request" {
fakeISC.MockMakeRequestFn = func(ctx context.Context, method, path string, body interface{}) (*http.Response, error) {
return nil, fmt.Errorf("an error occurred")
}
}

if tt.name == "Sad case: unable to update program tenant id" {
fakeISC.MockMakeRequestFn = func(ctx context.Context, method, path string, body interface{}) (*http.Response, error) {
jsonBody, err := json.Marshal(map[string]interface{}{"error": "error"})
if err != nil {
return nil, err
}
return &http.Response{
Status: "400",
StatusCode: 400,
Body: io.NopCloser(bytes.NewReader(jsonBody)),
}, nil
}
}

if err := s.UpdateProgramFHIRTenantID(tt.args.ctx, tt.args.programID, tt.args.tenantID); (err != nil) != tt.wantErr {
t.Errorf("ServiceMyCareHubImpl.UpdateProgramFHIRTenantID() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
1 change: 1 addition & 0 deletions pkg/clinical/infrastructure/services/pubsub/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ func (ps ServicePubSubMessaging) TopicIDs() []string {
ps.AddPubSubNamespace(common.TestResultTopicName, common.ClinicalServiceName),
ps.AddPubSubNamespace(common.TestOrderTopicName, common.ClinicalServiceName),
ps.AddPubSubNamespace(common.OrganizationTopicName, common.ClinicalServiceName),
ps.AddPubSubNamespace(common.TenantTopicName, common.ClinicalServiceName),
}
}

Expand Down
23 changes: 23 additions & 0 deletions pkg/clinical/presentation/rest/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,29 @@ func (p PresentationHandlersImpl) ReceivePubSubPushMessage(c *gin.Context) {
return
}

case utils.AddPubSubNamespace(common.TenantTopicName, common.ClinicalServiceName):
var data dto.OrganizationInput

err := json.Unmarshal(message.Message.Data, &data)
if err != nil {
serverutils.WriteJSONResponse(c.Writer, errorcodeutil.CustomError{
Err: err,
Message: err.Error(),
}, http.StatusBadRequest)

return
}

err = p.usecases.CreatePubsubTenant(ctx, data)
if err != nil {
serverutils.WriteJSONResponse(c.Writer, errorcodeutil.CustomError{
Err: err,
Message: err.Error(),
}, http.StatusBadRequest)

return
}

case utils.AddPubSubNamespace(common.TestResultTopicName, common.ClinicalServiceName):
var data dto.CreatePatientTestResultPubSubMessage

Expand Down
36 changes: 36 additions & 0 deletions pkg/clinical/presentation/rest/handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
fakeOCLMock "github.com/savannahghi/clinical/pkg/clinical/infrastructure/services/openconceptlab/mock"
"github.com/savannahghi/clinical/pkg/clinical/presentation"
"github.com/savannahghi/clinical/pkg/clinical/usecases"
"github.com/savannahghi/interserviceclient"
"github.com/savannahghi/pubsubtools"
"github.com/savannahghi/serverutils"
)
Expand Down Expand Up @@ -173,6 +174,16 @@ func TestPresentationHandlersImpl_ReceivePubSubPushMessage(t *testing.T) {
wantStatus: http.StatusOK,
wantErr: false,
},
{
name: "happy case: publish tenant message",
args: args{
url: "/pubsub",
httpMethod: http.MethodPost,
body: nil,
},
wantStatus: http.StatusOK,
wantErr: false,
},
{
name: "sad case: publish create results message",
args: args{
Expand Down Expand Up @@ -511,6 +522,31 @@ func TestPresentationHandlersImpl_ReceivePubSubPushMessage(t *testing.T) {
}
}

if tt.name == "happy case: publish tenant message" {
message := dto.OrganizationInput{
Name: gofakeit.BeerName(),
PhoneNumber: interserviceclient.TestUserPhoneNumber,
Identifiers: []dto.OrganizationIdentifier{
{
Type: dto.OrganizationIdentifierType("MCHProgram"),
Value: gofakeit.UUID(),
},
},
}
data, _ := json.Marshal(message)
fakeExt.MockVerifyPubSubJWTAndDecodePayloadFn = func(w http.ResponseWriter, r *http.Request) (*pubsubtools.PubSubPayload, error) {
return &pubsubtools.PubSubPayload{
Message: pubsubtools.PubSubMessage{
Data: data,
},
}, nil
}

fakeExt.MockGetPubSubTopicFn = func(m *pubsubtools.PubSubPayload) (string, error) {
return utils.AddPubSubNamespace(common.TenantTopicName, common.ClinicalServiceName), nil
}
}

if tt.name == "sad case: publish create results message" {
concept := "1234"
msg := dto.CreatePatientTestResultPubSubMessage{
Expand Down
24 changes: 24 additions & 0 deletions pkg/clinical/usecases/clinical/pubsub.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ func (c *UseCasesClinicalImpl) CreatePubsubPatient(ctx context.Context, payload
return nil
}

// CreatePubsubOrganization creates a FHIR organisation resource
func (c *UseCasesClinicalImpl) CreatePubsubOrganization(ctx context.Context, data dto.CreateFacilityPubSubMessage) error {
use := domain.ContactPointUseEnumWork
rank := int64(1)
Expand Down Expand Up @@ -107,6 +108,7 @@ func (c *UseCasesClinicalImpl) CreatePubsubOrganization(ctx context.Context, dat
return nil
}

// CreatePubsubVitals creates FHIR observation vitals.
func (c *UseCasesClinicalImpl) CreatePubsubVitals(ctx context.Context, data dto.CreateVitalSignPubSubMessage) error {
input, err := c.ComposeVitalsInput(ctx, data)
if err != nil {
Expand All @@ -121,6 +123,7 @@ func (c *UseCasesClinicalImpl) CreatePubsubVitals(ctx context.Context, data dto.
return nil
}

// CreatePubsubAllergyIntolerance creates FHIR allergy intolerance
func (c *UseCasesClinicalImpl) CreatePubsubAllergyIntolerance(ctx context.Context, data dto.CreatePatientAllergyPubSubMessage) error {
input, err := c.ComposeAllergyIntoleranceInput(ctx, data)
if err != nil {
Expand All @@ -135,6 +138,7 @@ func (c *UseCasesClinicalImpl) CreatePubsubAllergyIntolerance(ctx context.Contex
return nil
}

// CreatePubsubTestResult creates a test result as an observation
func (c *UseCasesClinicalImpl) CreatePubsubTestResult(ctx context.Context, data dto.CreatePatientTestResultPubSubMessage) error {
input, err := c.ComposeTestResultInput(ctx, data)
if err != nil {
Expand All @@ -149,6 +153,7 @@ func (c *UseCasesClinicalImpl) CreatePubsubTestResult(ctx context.Context, data
return nil
}

// CreatePubsubMedicationStatement creates a FHIR medication statement
func (c *UseCasesClinicalImpl) CreatePubsubMedicationStatement(ctx context.Context, data dto.CreateMedicationPubSubMessage) error {
input, err := c.ComposeMedicationStatementInput(ctx, data)
if err != nil {
Expand All @@ -162,3 +167,22 @@ func (c *UseCasesClinicalImpl) CreatePubsubMedicationStatement(ctx context.Conte

return nil
}

// CreatePubsubTenant registers a tenant in this service
func (c *UseCasesClinicalImpl) CreatePubsubTenant(ctx context.Context, data dto.OrganizationInput) error {
if data.Identifiers[0].Type != dto.OrganizationIdentifierType("MCHProgram") {
return fmt.Errorf("invalid identifier type %v", data.Identifiers[0].Type)
}

organization, err := c.RegisterTenant(ctx, data)
if err != nil {
return err
}

err = c.infrastructure.MyCareHub.UpdateProgramFHIRTenantID(ctx, data.Identifiers[0].Value, organization.ID)
if err != nil {
return err
}

return nil
}
Loading

0 comments on commit 9204b6a

Please sign in to comment.