Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: hookup sending patient referral form via SMS #428

Merged
merged 1 commit into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions pkg/clinical/application/dto/advantage.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,10 @@ type SegmentationPayload struct {
ClinicalID string `json:"clinical_id,omitempty"`
SegmentLabel SegmentationCategory `json:"segment_label,omitempty"`
}

// SMSPayload is used to model data used to send sms to patients through the advantage service
type SMSPayload struct {
Intention string `json:"intention"`
Message string `json:"message"`
Recipients []string `json:"recipients"`
}
Original file line number Diff line number Diff line change
Expand Up @@ -2334,6 +2334,7 @@ func NewFHIRMock() *FHIRMock {
},
MockSearchFHIRDocumentReferenceFn: func(ctx context.Context, searchParams map[string]interface{}, tenant dto.TenantIdentifiers, pagination dto.Pagination) (*domain.PagedFHIRDocumentReference, error) {
resourceID := gofakeit.UUID()
URL := gofakeit.URL()
return &domain.PagedFHIRDocumentReference{
DocumentReferences: []domain.FHIRDocumentReference{
{
Expand All @@ -2346,6 +2347,17 @@ func NewFHIRMock() *FHIRMock {
},
},
},
Subject: &domain.FHIRReference{
ID: &resourceID,
},
Content: []domain.FHIRDocumentReferenceContent{
{
ID: resourceID,
Attachment: domain.FHIRAttachment{
URL: (*scalarutils.URL)(&URL),
},
},
},
},
},
HasNextPage: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
// FakeAdvantage mocks the implementation of advantage API methods
type FakeAdvantage struct {
MockSegmentPatient func(ctx context.Context, payload dto.SegmentationPayload) error
MockSendSMSFn func(ctx context.Context, workstationID string, payload dto.SMSPayload) error
}

// NewFakeAdvantageMock is the advantage's mock methods constructor
Expand All @@ -17,10 +18,18 @@ func NewFakeAdvantageMock() *FakeAdvantage {
MockSegmentPatient: func(ctx context.Context, payload dto.SegmentationPayload) error {
return nil
},
MockSendSMSFn: func(ctx context.Context, workstationID string, payload dto.SMSPayload) error {
return nil
},
}
}

// SegmentPatient mocks the implementation of patient segmentation usecase
func (f *FakeAdvantage) SegmentPatient(ctx context.Context, payload dto.SegmentationPayload) error {
return f.MockSegmentPatient(ctx, payload)
}

// SendSMS mocks the implementation of SMS notification to patient
func (f *FakeAdvantage) SendSMS(ctx context.Context, workstationID string, payload dto.SMSPayload) error {
return f.MockSendSMSFn(ctx, workstationID, payload)
}
41 changes: 40 additions & 1 deletion pkg/clinical/infrastructure/services/advantage/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
var (
AdvantageBaseURL = serverutils.MustGetEnvVar("ADVANTAGE_BASE_URL")
segmentationPath = "/api/segments/segment/clinical/"
smsPath = "/api/notifications/sms/"
)

// AuthUtilsLib holds the method defined in authutils library
Expand All @@ -26,6 +27,7 @@ type AuthUtilsLib interface {
// AdvantageService represents methods that can be used to communicate with the advantage server
type AdvantageService interface {
SegmentPatient(ctx context.Context, payload dto.SegmentationPayload) error
SendSMS(ctx context.Context, workstationID string, payload dto.SMSPayload) error
}

// ServiceAdvantageImpl represents advantage server's implementations
Expand All @@ -46,7 +48,6 @@ func (s *ServiceAdvantageImpl) SegmentPatient(ctx context.Context, payload dto.S

payloadBytes, err := json.Marshal(payload)
if err != nil {
fmt.Println("Error marshaling JSON:", err)
return err
}

Expand Down Expand Up @@ -77,3 +78,41 @@ func (s *ServiceAdvantageImpl) SegmentPatient(ctx context.Context, payload dto.S

return nil
}

// SendSMS is used to send an SMS message to a patient.
func (s *ServiceAdvantageImpl) SendSMS(ctx context.Context, workstationID string, payload dto.SMSPayload) error {
url := fmt.Sprintf("%s/%s", AdvantageBaseURL, smsPath)

payloadBytes, err := json.Marshal(payload)
if err != nil {
return err
}

body := bytes.NewReader(payloadBytes)

req, err := http.NewRequest(http.MethodPost, url, body)
if err != nil {
return err
}

token, err := s.client.Authenticate()
if err != nil {
return err
}

req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token.AccessToken))
req.Header.Set("X-Workstation", workstationID)

httpClient := &http.Client{Timeout: time.Second * 30}

resp, err := httpClient.Do(req)
if err != nil {
return err
}

defer resp.Body.Close()

return nil
}
56 changes: 56 additions & 0 deletions pkg/clinical/infrastructure/services/advantage/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,59 @@ func TestServiceAdvantageImpl_PatientSegmentation(t *testing.T) {
})
}
}

func TestServiceAdvantageImpl_SendSMS(t *testing.T) {
type args struct {
ctx context.Context
payload dto.SMSPayload
workstationID string
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "Happy case: send SMS",
args: args{
ctx: context.Background(),
payload: dto.SMSPayload{
Intention: "DIRECT_MESSAGE",
Message: "message",
Recipients: []string{},
},
workstationID: gofakeit.UUID(),
},
wantErr: false,
},
{
name: "Sad case: unable to send SMS",
args: args{
ctx: context.Background(),
payload: dto.SMSPayload{
Intention: "DIRECT_MESSAGE",
Message: "message",
Recipients: []string{},
},
workstationID: gofakeit.UUID(),
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fakeAuthUtils := advantageMock.NewAuthUtilsClientMock()
s := advantage.NewServiceAdvantage(fakeAuthUtils)

if tt.name == "Sad case: unable to send SMS" {
fakeAuthUtils.MockAuthenticateFn = func() (*authutils.OAUTHResponse, error) {
return nil, errors.New("unable to authenticate")
}
}

if err := s.SendSMS(tt.args.ctx, tt.args.workstationID, tt.args.payload); (err != nil) != tt.wantErr {
t.Errorf("ServiceAdvantageImpl.SendSMS() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
2 changes: 1 addition & 1 deletion pkg/clinical/presentation/graph/clinical.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -226,5 +226,5 @@ extend type Mutation {
# Referral
referPatient(input: ReferralInput!): ServiceRequest!

shareReferralForm(serviceRequestID: ID!): Boolean!
shareReferralForm(serviceRequestID: ID!, workstationID: String!): Boolean!
}
4 changes: 2 additions & 2 deletions pkg/clinical/presentation/graph/clinical.resolvers.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 14 additions & 5 deletions pkg/clinical/presentation/graph/generated/generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 38 additions & 4 deletions pkg/clinical/usecases/clinical/referral_report.go
Original file line number Diff line number Diff line change
Expand Up @@ -349,9 +349,9 @@ func (c *UseCasesClinicalImpl) CreateDocumentReference(ctx context.Context, payl
return nil
}

// ShareReferralForm is searched for a document reference associated with a service request, retrieves the document URL and sends it to the
// patient via SMS
func (c *UseCasesClinicalImpl) ShareReferralForm(ctx context.Context, serviceRequestID string) (bool, error) {
// ShareReferralForm is searches for a document reference using the service request ID associated and retrieves the document URL
// (which is the url of the referral form that we want to share) and sends it to the patient via SMS
func (c *UseCasesClinicalImpl) ShareReferralForm(ctx context.Context, serviceRequestID string, workstationID string) (bool, error) {
identifiers, err := c.infrastructure.BaseExtension.GetTenantIdentifiers(ctx)
if err != nil {
return false, err
Expand All @@ -373,7 +373,41 @@ func (c *UseCasesClinicalImpl) ShareReferralForm(ctx context.Context, serviceReq
return false, errors.New("no document reference found")
}

// TODO: Send SMS here
var patientID string
if output.DocumentReferences[0].Subject != nil {
patientID = *output.DocumentReferences[0].Subject.ID
} else {
return false, errors.New("no subject found")
}

patient, err := c.infrastructure.FHIR.GetFHIRPatient(ctx, patientID)
if err != nil {
utils.ReportErrorToSentry(err)
return false, err
}

var message string

if len(output.DocumentReferences) > 0 && output.DocumentReferences[0].Content[0].Attachment.URL != nil {
message = string(*output.DocumentReferences[0].Content[0].Attachment.URL)
}

var recipients []string

if len(patient.Resource.Telecom) > 0 && patient.Resource.Telecom[0].Value != nil {
message = *patient.Resource.Telecom[0].Value
}

smsPayload := &dto.SMSPayload{
Intention: "DIRECT_MESSAGE",
Message: message,
Recipients: recipients,
}

err = c.infrastructure.AdvantageService.SendSMS(ctx, workstationID, *smsPayload)
if err != nil {
return false, err
}

return true, nil
}
Loading
Loading