diff --git a/pkg/clinical/domain/questionnaire.go b/pkg/clinical/domain/questionnaire.go new file mode 100644 index 00000000..24b0a497 --- /dev/null +++ b/pkg/clinical/domain/questionnaire.go @@ -0,0 +1,126 @@ +package domain + +import ( + "time" + + "github.com/savannahghi/scalarutils" +) + +// FHIRQuestionnaire models the FHIR questionnaire model as described in https://www.hl7.org/fhir/questionnaire.html +type FHIRQuestionnaire struct { + ID *string `json:"id,omitempty"` + Meta *FHIRMetaInput `json:"meta,omitempty"` + ImplicitRules *string `json:"implicitRules,omitempty"` + Language *string `json:"language,omitempty"` + Text *FHIRNarrative `json:"text,omitempty"` + FHIRExtension []*Extension `json:"extension,omitempty"` + ModifierExtension []*Extension `json:"modifierExtension,omitempty"` + URL *scalarutils.URI `json:"url,omitempty"` + Identifier []*FHIRIdentifier `json:"identifier,omitempty"` + Version *string `json:"version,omitempty"` + Name *string `json:"name,omitempty"` + Title *string `json:"title,omitempty"` + DerivedFrom []*string `json:"derivedFrom,omitempty"` + Status *scalarutils.Code `json:"status,omitempty"` + Experimental *bool `json:"experimental,omitempty"` + Date *scalarutils.DateTime `json:"date,omitempty"` + Publisher *string `json:"publisher,omitempty"` + Description *string `json:"description,omitempty"` + UseContext *FHIRUsageContext `json:"useContext,omitempty"` + Jurisdiction []*FHIRCodeableConcept `json:"jurisdiction,omitempty"` + Purpose *string `json:"purpose,omitempty"` + EffectivePeriod *FHIRPeriod `json:"effectivePeriod,omitempty"` + Code []*FHIRCoding `json:"code,omitempty"` + Item []*FHIRQuestionnaireItem `json:"item,omitempty"` +} + +// FHIRQuestionnaireItem represents the questions and sections within a FHIR questionnaire +type FHIRQuestionnaireItem struct { + ID *string `json:"id,omitempty"` + Meta *FHIRMeta `json:"meta,omitempty"` + FHIRExtension []*Extension `json:"extension,omitempty"` + ModifierExtension []*Extension `json:"modifierExtension,omitempty"` + LinkID *string `json:"linkId,omitempty"` + Definition *scalarutils.URI `json:"definition,omitempty"` + Code []*FHIRCoding `json:"code,omitempty"` + Prefix *string `json:"prefix,omitempty"` + Text *string `json:"text,omitempty"` + Type *scalarutils.Code `json:"type,omitempty"` + EnableWhen []*FHIRQuestionnaireItemEnableWhen `json:"enableWhen,omitempty"` + EnableBehavior *scalarutils.Code `json:"enableBehavior,omitempty"` + DisabledDisplay *scalarutils.Code `json:"disabledDisplay,omitempty"` + Required *bool `json:"required,omitempty"` + Repeats *bool `json:"repeats,omitempty"` + ReadOnly *bool `json:"readOnly,omitempty"` + MaxLength *int `json:"maxLength,omitempty"` + AnswerValueSet *string `json:"answerValueSet,omitempty"` + AnswerOption []*FHIRQuestionnaireItemAnswerOption `json:"answerOption,omitempty"` + Initial []*FHIRQuestionnaireItemInitial `json:"initial,omitempty"` + Item []*FHIRQuestionnaireItem `json:"item,omitempty"` +} + +// FHIRQuestionnaireItemEnableWhen defines when to enable the FHIR Questionnaire item. +type FHIRQuestionnaireItemEnableWhen struct { + ID *string `json:"id,omitempty"` + FHIRExtension []*Extension `json:"extension,omitempty"` + ModifierExtension []*Extension `json:"modifierExtension,omitempty"` + Question *string `json:"question,omitempty"` + Operator *scalarutils.Code `json:"operator,omitempty"` + AnswerBoolean *bool `json:"answerBoolean,omitempty"` + AnswerDecimal *float64 `json:"answerDecimal,omitempty"` + AnswerInteger *int `json:"answerInteger,omitempty"` + AnswerDate *scalarutils.Date `json:"answerDate,omitempty"` + AnswerDateTime *scalarutils.DateTime `json:"answerDateTime,omitempty"` + AnswerTime *scalarutils.DateTime `json:"answerTime,omitempty"` + AnswerString *string `json:"answerString,omitempty"` + AnswerCoding *FHIRCoding `json:"answerCoding,omitempty"` + AnswerQuantity *FHIRQuantity `json:"answerQuantity,omitempty"` + AnswerReference *FHIRReference `json:"answerReference,omitempty"` +} + +// FHIRQuestionnaireItemAnswerOption represents the permitted answers to a questionnaire. +// ! Rule: A question cannot have both answerOption and answerValueSet +// ! Rule: Only coding, decimal, integer, date, dateTime, time, string or quantity items can have answerOption or answerValueSet +// ! Rule: If one or more answerOption is present, initial cannot be present. Use answerOption.initialSelected instead +type FHIRQuestionnaireItemAnswerOption struct { + ID *string `json:"id,omitempty"` + FHIRExtension []*Extension `json:"extension,omitempty"` + ModifierExtension []*Extension `json:"modifierExtension,omitempty"` + ValueInteger *int `json:"valueInteger,omitempty"` + ValueDate *scalarutils.Date `json:"valueDate,omitempty"` + ValueTime *time.Time `json:"valueTime,omitempty"` + ValueString string `json:"valueString,omitempty"` + ValueCoding *FHIRCoding `json:"valueCoding,omitempty"` + ValueReference *FHIRReference `json:"valueReference,omitempty"` + InitialSelected *bool `json:"initialSelected,omitempty"` +} + +// FHIRQuestionnaireItemInitial defines the initial value(s) when a questionnaire item is first rendered +type FHIRQuestionnaireItemInitial struct { + ID *string `json:"id,omitempty"` + FHIRExtension []*Extension `json:"extension,omitempty"` + ModifierExtension []*Extension `json:"modifierExtension,omitempty"` + ValueBoolean *bool `json:"valueBoolean,omitempty"` + ValueDecimal *float64 `json:"valueDecimal,omitempty"` + ValueInteger *int `json:"valueInteger,omitempty"` + ValueDate *scalarutils.Date `json:"valueDate,omitempty"` + ValueDateTime *scalarutils.DateTime `json:"valueDateTime,omitempty"` + ValueTime *time.Time `json:"valueTime,omitempty"` + ValueString string `json:"valueString,omitempty"` + ValueURI *scalarutils.URI `json:"valueUri,omitempty"` + ValueAttachment *FHIRAttachment `json:"valueAttachment,omitempty"` + ValueCoding *FHIRCoding `json:"valueCoding,omitempty"` + ValueQuantity *FHIRQuantity `json:"valueQuantity,omitempty"` + ValueReference *FHIRReference `json:"valueReference,omitempty"` +} + +// FHIRUsageContext describes the context that the questionnaire content is intended to support +type FHIRUsageContext struct { + ID *string `json:"id,omitempty"` + FHIRExtension []*Extension `json:"extension,omitempty"` + Code *FHIRCoding `json:"code,omitempty"` + ValueCodeableConcept *FHIRCodeableConcept `json:"valueCodeableConcept,omitempty"` + ValueQuantity *FHIRQuantity `json:"valueQuantity,omitempty"` + ValueRange *FHIRRange `json:"valueRange,omitempty"` + ValueReference *FHIRReference `json:"valueReference,omitempty"` +} diff --git a/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhir.go b/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhir.go index 6f3bcce3..37fef0d3 100644 --- a/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhir.go +++ b/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhir.go @@ -35,6 +35,7 @@ const ( medicationStatementResourceType = "MedicationStatement" medicationResourceType = "Medication" mediaResourceType = "Media" + questionnaireResourceType = "Questionnaire" ) // Dataset ... @@ -1682,3 +1683,20 @@ func (fh StoreImpl) PatchFHIRObservation(_ context.Context, id string, input dom return resource, nil } + +// CreateFHIRQuestionnaire is used to create a FHIR Questionnaire resource +func (fh StoreImpl) CreateFHIRQuestionnaire(_ context.Context, input *domain.FHIRQuestionnaire) (*domain.FHIRQuestionnaire, error) { + payload, err := converterandformatter.StructToMap(input) + if err != nil { + return nil, fmt.Errorf("unable to turn %s input into a map: %w", questionnaireResourceType, err) + } + + resource := &domain.FHIRQuestionnaire{} + + err = fh.Dataset.CreateFHIRResource(questionnaireResourceType, payload, resource) + if err != nil { + return nil, fmt.Errorf("unable to create %s resource: %w", questionnaireResourceType, err) + } + + return resource, nil +} diff --git a/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhir_unit_test.go b/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhir_unit_test.go index 070d4489..cbc331ab 100644 --- a/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhir_unit_test.go +++ b/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhir_unit_test.go @@ -4390,3 +4390,80 @@ func TestStoreImpl_SearchPatientMedia(t *testing.T) { }) } } + +func TestStoreImpl_CreateFHIRQuestionnaire(t *testing.T) { + ID := gofakeit.UUID() + type args struct { + ctx context.Context + input *domain.FHIRQuestionnaire + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "Happy case: successfully create a questionnaire resource", + args: args{ + ctx: context.Background(), + input: &domain.FHIRQuestionnaire{ + ID: &ID, + Meta: &domain.FHIRMetaInput{}, + ImplicitRules: new(string), + Language: new(string), + Text: &domain.FHIRNarrative{}, + FHIRExtension: []*domain.Extension{}, + ModifierExtension: []*domain.Extension{}, + Identifier: []*domain.FHIRIdentifier{}, + Version: new(string), + Name: new(string), + Title: new(string), + DerivedFrom: []*string{}, + Experimental: new(bool), + Publisher: new(string), + Description: new(string), + UseContext: &domain.FHIRUsageContext{}, + Jurisdiction: []*domain.FHIRCodeableConcept{}, + Purpose: new(string), + EffectivePeriod: &domain.FHIRPeriod{}, + Code: []*domain.FHIRCoding{}, + Item: []*domain.FHIRQuestionnaireItem{}, + }, + }, + wantErr: false, + }, + { + name: "Sad case: unable to create a questionnaire resource", + args: args{ + ctx: context.Background(), + input: &domain.FHIRQuestionnaire{ + ID: &ID, + Meta: &domain.FHIRMetaInput{}, + ImplicitRules: new(string), + Language: new(string), + Text: &domain.FHIRNarrative{}, + FHIRExtension: []*domain.Extension{}, + }, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fakeDataset := fakeDataset.NewFakeFHIRRepositoryMock() + fh := FHIR.NewFHIRStoreImpl(fakeDataset) + + if tt.name == "Sad case: unable to create a questionnaire resource" { + fakeDataset.MockCreateFHIRResourceFn = func(resourceType string, payload map[string]interface{}, resource interface{}) error { + return fmt.Errorf("an error ocurred") + } + } + + _, err := fh.CreateFHIRQuestionnaire(tt.args.ctx, tt.args.input) + if (err != nil) != tt.wantErr { + t.Errorf("StoreImpl.CreateFHIRQuestionnaire() error = %v, wantErr %v", err, tt.wantErr) + return + } + }) + } +} diff --git a/pkg/clinical/infrastructure/datastore/cloudhealthcare/mock/fhir_mock.go b/pkg/clinical/infrastructure/datastore/cloudhealthcare/mock/fhir_mock.go index d93b759e..49fd32ee 100644 --- a/pkg/clinical/infrastructure/datastore/cloudhealthcare/mock/fhir_mock.go +++ b/pkg/clinical/infrastructure/datastore/cloudhealthcare/mock/fhir_mock.go @@ -81,6 +81,7 @@ type FHIRMock struct { MockSearchPatientAllergyIntoleranceFn func(ctx context.Context, patientReference string, tenant dto.TenantIdentifiers, pagination dto.Pagination) (*domain.PagedFHIRAllergy, error) MockCreateFHIRMediaFn func(ctx context.Context, input domain.FHIRMedia) (*domain.FHIRMedia, error) MockSearchPatientMediaFn func(ctx context.Context, patientReference string, tenant dto.TenantIdentifiers, pagination dto.Pagination) (*domain.PagedFHIRMedia, error) + MockCreateFHIRQuestionnaireFn func(ctx context.Context, input *domain.FHIRQuestionnaire) (*domain.FHIRQuestionnaire, error) } // NewFHIRMock initializes a new instance of FHIR mock @@ -1841,6 +1842,31 @@ func NewFHIRMock() *FHIRMock { TotalCount: 0, }, nil }, + MockCreateFHIRQuestionnaireFn: func(ctx context.Context, input *domain.FHIRQuestionnaire) (*domain.FHIRQuestionnaire, error) { + return &domain.FHIRQuestionnaire{ + ID: new(string), + Meta: &domain.FHIRMetaInput{}, + ImplicitRules: new(string), + Language: new(string), + Text: &domain.FHIRNarrative{}, + FHIRExtension: []*domain.Extension{}, + ModifierExtension: []*domain.Extension{}, + Identifier: []*domain.FHIRIdentifier{}, + Version: new(string), + Name: new(string), + Title: new(string), + DerivedFrom: []*string{}, + Experimental: new(bool), + Publisher: new(string), + Description: new(string), + UseContext: &domain.FHIRUsageContext{}, + Jurisdiction: []*domain.FHIRCodeableConcept{}, + Purpose: new(string), + EffectivePeriod: &domain.FHIRPeriod{}, + Code: []*domain.FHIRCoding{}, + Item: []*domain.FHIRQuestionnaireItem{}, + }, nil + }, } } @@ -2143,3 +2169,8 @@ func (fh *FHIRMock) CreateFHIRMedia(ctx context.Context, input domain.FHIRMedia) func (fh *FHIRMock) SearchPatientMedia(ctx context.Context, patientReference string, tenant dto.TenantIdentifiers, pagination dto.Pagination) (*domain.PagedFHIRMedia, error) { return fh.MockSearchPatientMediaFn(ctx, patientReference, tenant, pagination) } + +// CreateFHIRQuestionnaire mocks the creation of a new Questionnaire resource. +func (fh *FHIRMock) CreateFHIRQuestionnaire(ctx context.Context, input *domain.FHIRQuestionnaire) (*domain.FHIRQuestionnaire, error) { + return fh.MockCreateFHIRQuestionnaireFn(ctx, input) +} diff --git a/pkg/clinical/presentation/config.go b/pkg/clinical/presentation/config.go index fc0db7cd..32591d2b 100644 --- a/pkg/clinical/presentation/config.go +++ b/pkg/clinical/presentation/config.go @@ -186,6 +186,11 @@ func SetupRoutes(r *gin.Engine, cacheStore persist.CacheStore, authclient *authu upload.Use(rest.AuthenticationGinMiddleware(cacheStore, *authclient)) upload.Use(rest.TenantIdentifierExtractionMiddleware(infra.FHIR)) upload.POST("", handlers.UploadMedia) + + questionnaire := v1.Group("/questionnaire") + questionnaire.Use(rest.AuthenticationGinMiddleware(cacheStore, *authclient)) + questionnaire.Use(rest.TenantIdentifierExtractionMiddleware(infra.FHIR)) + questionnaire.POST("", handlers.LoadQuestionnaire) } // GQLHandler sets up a GraphQL resolver diff --git a/pkg/clinical/presentation/rest/handlers.go b/pkg/clinical/presentation/rest/handlers.go index d4003bb8..3dda5ba1 100644 --- a/pkg/clinical/presentation/rest/handlers.go +++ b/pkg/clinical/presentation/rest/handlers.go @@ -10,6 +10,7 @@ import ( "github.com/savannahghi/clinical/pkg/clinical/application/common" "github.com/savannahghi/clinical/pkg/clinical/application/dto" "github.com/savannahghi/clinical/pkg/clinical/application/utils" + "github.com/savannahghi/clinical/pkg/clinical/domain" "github.com/savannahghi/clinical/pkg/clinical/usecases" "github.com/savannahghi/errorcodeutil" "github.com/savannahghi/pubsubtools" @@ -312,3 +313,22 @@ func (p PresentationHandlersImpl) UploadMedia(c *gin.Context) { c.JSON(http.StatusOK, response) } + +// LoadQuestionnaire is used to upload a user defined questionnaire for the purpose of soliciting client data. +func (p PresentationHandlersImpl) LoadQuestionnaire(c *gin.Context) { + input := domain.FHIRQuestionnaire{} + + err := c.BindJSON(&input) + if err != nil { + jsonErrorResponse(c, http.StatusBadRequest, err) + return + } + + questionnaire, err := p.usecases.CreateQuestionnaire(c.Request.Context(), &input) + if err != nil { + jsonErrorResponse(c, http.StatusBadRequest, err) + return + } + + c.JSON(http.StatusOK, questionnaire) +} diff --git a/pkg/clinical/repository/repository.go b/pkg/clinical/repository/repository.go index 02d29d8f..4b44121c 100644 --- a/pkg/clinical/repository/repository.go +++ b/pkg/clinical/repository/repository.go @@ -22,6 +22,7 @@ type FHIR interface { FHIRMedicationStatement FHIRMedication FHIRMedia + FHIRQuestionnaire } type FHIROrganization interface { @@ -111,3 +112,7 @@ type FHIRMedia interface { CreateFHIRMedia(ctx context.Context, input domain.FHIRMedia) (*domain.FHIRMedia, error) SearchPatientMedia(ctx context.Context, patientReference string, tenant dto.TenantIdentifiers, pagination dto.Pagination) (*domain.PagedFHIRMedia, error) } + +type FHIRQuestionnaire interface { + CreateFHIRQuestionnaire(ctx context.Context, input *domain.FHIRQuestionnaire) (*domain.FHIRQuestionnaire, error) +} diff --git a/pkg/clinical/usecases/clinical/questionnaire.go b/pkg/clinical/usecases/clinical/questionnaire.go new file mode 100644 index 00000000..56249f4b --- /dev/null +++ b/pkg/clinical/usecases/clinical/questionnaire.go @@ -0,0 +1,27 @@ +package clinical + +import ( + "context" + + "github.com/savannahghi/clinical/pkg/clinical/domain" +) + +// CreateQuestionnaire is used to create a new Questionnaire. +// These questionnaire are used to solicit various types of information from patients to server organisation usecases. +func (q *UseCasesClinicalImpl) CreateQuestionnaire(ctx context.Context, questionnaireInput *domain.FHIRQuestionnaire) (*domain.FHIRQuestionnaire, error) { + tags, err := q.GetTenantMetaTags(ctx) + if err != nil { + return nil, err + } + + questionnaireInput.Meta = &domain.FHIRMetaInput{ + Tag: tags, + } + + questionnaire, err := q.infrastructure.FHIR.CreateFHIRQuestionnaire(ctx, questionnaireInput) + if err != nil { + return nil, err + } + + return questionnaire, nil +} diff --git a/pkg/clinical/usecases/clinical/questionnaire_test.go b/pkg/clinical/usecases/clinical/questionnaire_test.go new file mode 100644 index 00000000..3c8f5dbe --- /dev/null +++ b/pkg/clinical/usecases/clinical/questionnaire_test.go @@ -0,0 +1,135 @@ +package clinical_test + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/brianvoe/gofakeit" + "github.com/savannahghi/clinical/pkg/clinical/application/dto" + fakeExtMock "github.com/savannahghi/clinical/pkg/clinical/application/extensions/mock" + "github.com/savannahghi/clinical/pkg/clinical/domain" + "github.com/savannahghi/clinical/pkg/clinical/infrastructure" + fakeFHIRMock "github.com/savannahghi/clinical/pkg/clinical/infrastructure/datastore/cloudhealthcare/mock" + fakeOCLMock "github.com/savannahghi/clinical/pkg/clinical/infrastructure/services/openconceptlab/mock" + fakePubSubMock "github.com/savannahghi/clinical/pkg/clinical/infrastructure/services/pubsub/mock" + fakeUploadMock "github.com/savannahghi/clinical/pkg/clinical/infrastructure/services/upload/mock" + clinicalUsecase "github.com/savannahghi/clinical/pkg/clinical/usecases/clinical" +) + +func TestUseCasesClinicalImpl_CreateQuestionnaire(t *testing.T) { + ID := gofakeit.UUID() + type args struct { + ctx context.Context + questionnaireInput *domain.FHIRQuestionnaire + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "Happy case: create questionnaire", + args: args{ + ctx: addTenantIdentifierContext(context.Background()), + questionnaireInput: &domain.FHIRQuestionnaire{ + ID: &ID, + Meta: &domain.FHIRMetaInput{ + VersionID: ID, + LastUpdated: time.Now(), + Source: "", + Tag: []domain.FHIRCodingInput{ + { + ID: &ID, + Version: &ID, + Code: "", + Display: "", + UserSelected: new(bool), + }, + }, + }, + }, + }, + wantErr: false, + }, + { + name: "Sad case: unable to get tenant tags", + args: args{ + ctx: addTenantIdentifierContext(context.Background()), + questionnaireInput: &domain.FHIRQuestionnaire{ + ID: &ID, + Meta: &domain.FHIRMetaInput{ + VersionID: ID, + LastUpdated: time.Now(), + Source: "", + Tag: []domain.FHIRCodingInput{ + { + ID: &ID, + Version: &ID, + Code: "", + Display: "", + UserSelected: new(bool), + }, + }, + }, + }, + }, + wantErr: true, + }, + { + name: "Sad case: unable to create questionnaire", + args: args{ + ctx: addTenantIdentifierContext(context.Background()), + questionnaireInput: &domain.FHIRQuestionnaire{ + ID: &ID, + Meta: &domain.FHIRMetaInput{ + VersionID: ID, + LastUpdated: time.Now(), + Source: "", + Tag: []domain.FHIRCodingInput{ + { + ID: &ID, + Version: &ID, + Code: "", + Display: "", + UserSelected: new(bool), + }, + }, + }, + }, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fakeExt := fakeExtMock.NewFakeBaseExtensionMock() + fakeFHIR := fakeFHIRMock.NewFHIRMock() + fakeOCL := fakeOCLMock.NewFakeOCLMock() + fakePubSub := fakePubSubMock.NewPubSubServiceMock() + + fakeUpload := fakeUploadMock.NewFakeUploadMock() + + infra := infrastructure.NewInfrastructureInteractor(fakeExt, fakeFHIR, fakeOCL, fakeUpload, fakePubSub) + q := clinicalUsecase.NewUseCasesClinicalImpl(infra) + + if tt.name == "Sad case: unable to get tenant tags" { + fakeExt.MockGetTenantIdentifiersFn = func(ctx context.Context) (*dto.TenantIdentifiers, error) { + return nil, fmt.Errorf("an error occurred") + } + } + if tt.name == "Sad case: unable to create questionnaire" { + fakeFHIR.MockCreateFHIRQuestionnaireFn = func(ctx context.Context, input *domain.FHIRQuestionnaire) (*domain.FHIRQuestionnaire, error) { + return nil, fmt.Errorf("an error occurred") + } + } + + _, err := q.CreateQuestionnaire(tt.args.ctx, tt.args.questionnaireInput) + if (err != nil) != tt.wantErr { + t.Errorf("UseCasesClinicalImpl.CreateQuestionnaire() error = %v, wantErr %v", err, tt.wantErr) + return + } + }) + } +} diff --git a/pkg/clinical/usecases/usecases.go b/pkg/clinical/usecases/usecases.go index 99fa25cc..5eebd9d4 100644 --- a/pkg/clinical/usecases/usecases.go +++ b/pkg/clinical/usecases/usecases.go @@ -5,6 +5,7 @@ import ( "io" "github.com/savannahghi/clinical/pkg/clinical/application/dto" + "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/scalarutils" @@ -93,6 +94,9 @@ type Clinical interface { PatchPatientDiastolicBloodPressure(ctx context.Context, id string, value string) (*dto.Observation, error) PatchPatientSystolicBloodPressure(ctx context.Context, id string, value string) (*dto.Observation, error) PatchPatientRespiratoryRate(ctx context.Context, id string, value string) (*dto.Observation, error) + + // Questionnaire + CreateQuestionnaire(ctx context.Context, questionnaireInput *domain.FHIRQuestionnaire) (*domain.FHIRQuestionnaire, error) } // Interactor is an implementation of the usecases interface