diff --git a/gqlgen.yml b/gqlgen.yml index 4dd22c76..47454d65 100644 --- a/gqlgen.yml +++ b/gqlgen.yml @@ -19,10 +19,10 @@ resolver: dir: pkg/clinical/presentation/graph autobind: + - "github.com/savannahghi/clinical/pkg/clinical/application/dto" - "github.com/savannahghi/scalarutils" - "github.com/savannahghi/enumutils" - "github.com/savannahghi/firebasetools" - - "github.com/savannahghi/clinical/pkg/clinical/application/dto" - "github.com/savannahghi/clinical/pkg/clinical/domain" - "github.com/savannahghi/clinical/pkg/clinical/presentation/graph" - "github.com/savannahghi/clinical/pkg/clinical/infrastructure/services/openconceptlab" diff --git a/pkg/clinical/application/common/common.go b/pkg/clinical/application/common/common.go index 8839cd54..2937ffc2 100644 --- a/pkg/clinical/application/common/common.go +++ b/pkg/clinical/application/common/common.go @@ -5,11 +5,6 @@ import ( "github.com/sirupsen/logrus" ) -const ( - // MaxClinicalRecordPageSize is the maximum number of encounters we can show on a timeline - MaxClinicalRecordPageSize = 50 -) - // BaseExtension is an interface that represents some methods in base // The `onboarding` service has a dependency on `base` library. // Our first step to making some functions are testable is to remove the base dependency. diff --git a/pkg/clinical/application/common/defaults.go b/pkg/clinical/application/common/defaults.go index 6a3a30b3..46844914 100644 --- a/pkg/clinical/application/common/defaults.go +++ b/pkg/clinical/application/common/defaults.go @@ -16,16 +16,6 @@ const ( timeFormatStr = "2006-01-02T15:04:05+03:00" healthCloudIdentifiers = "healthcloud.identifiers" healthCloudIdentifiersVersion = "0.0.1" - // SendEmailEndpoint is the endpoint used to send an email - SendEmailEndpoint = "internal/send_email" - // SendSMSEndpoint is the endpoint used to send sms - SendSMSEndpoint = "internal/send_sms" - // EmailWelcomeSubject is the subject of the welcome email - EmailWelcomeSubject = "Welcome to Be.Well" - // DefaultLanguage ... - DefaultLanguage = "English" - // CallCenterNumber is Savannah's call center number - CallCenterNumber = "0790 360 360" // CreatePatientTopic is the topic ID where patient data is published to CreatePatientTopic = "patient.create" diff --git a/pkg/clinical/application/common/helpers/helpers.go b/pkg/clinical/application/common/helpers/helpers.go index de4c3e95..521c8e28 100644 --- a/pkg/clinical/application/common/helpers/helpers.go +++ b/pkg/clinical/application/common/helpers/helpers.go @@ -36,8 +36,6 @@ const ( IDIdentifierSystem = "healthcloud.iddocument" MSISDNIdentifierSystem = "healthcloud.msisdn" DefaultVersion = "0.0.1" - DefaultPhotoTitle = "Patient Photo" - DefaultPhotoFilename = "photo.jpg" ) // ComposeOneHealthEpisodeOfCare is used to create an episode of care diff --git a/pkg/clinical/application/dto/enums.go b/pkg/clinical/application/dto/enums.go index a4250dd3..a168d7ea 100644 --- a/pkg/clinical/application/dto/enums.go +++ b/pkg/clinical/application/dto/enums.go @@ -79,3 +79,30 @@ const ( MedicationStatementStatusEnumInActive MedicationStatementStatusEnum = "inactive" MedicationStatementStatusEnumUnknown MedicationStatementStatusEnum = "unknown" ) + +type IdentifierType string + +const ( + IdentifierTypeNationalID IdentifierType = "NATIONAL_ID" + IdentifierTypePassport IdentifierType = "PASSPORT" + IdentifierTypeAlienID IdentifierType = "ALIEN_ID" + IdentifierTypeCCCNumber IdentifierType = "CCC_NUMBER" +) + +type ContactType string + +const ( + ContactTypePhoneNumber ContactType = "PHONE_NUMBER" +) + +// Gender is a FHIR enum +type Gender string + +const ( + // GenderMale ... + GenderMale Gender = "male" + // GenderFemale ... + GenderFemale Gender = "female" + // GenderOther ... + GenderOther Gender = "other" +) diff --git a/pkg/clinical/application/dto/input.go b/pkg/clinical/application/dto/input.go index c3174a73..f90d52fe 100644 --- a/pkg/clinical/application/dto/input.go +++ b/pkg/clinical/application/dto/input.go @@ -1,6 +1,9 @@ package dto -import "github.com/go-playground/validator" +import ( + "github.com/go-playground/validator" + "github.com/savannahghi/scalarutils" +) type OrganizationIdentifier struct { Type OrganizationIdentifierType `json:"type,omitempty"` @@ -31,3 +34,23 @@ func (o ObservationInput) Validate() error { return err } + +type PatientInput struct { + FirstName string `json:"firstName"` + LastName string `json:"lastName"` + OtherNames *string `json:"otherNames"` + BirthDate scalarutils.Date `json:"birthDate"` + Gender Gender `json:"gender"` + Identifiers []IdentifierInput `json:"identifiers"` + Contacts []ContactInput `json:"contacts"` +} + +type IdentifierInput struct { + Type IdentifierType `json:"type"` + Value string `json:"value"` +} + +type ContactInput struct { + Type ContactType `json:"type"` + Value string `json:"value"` +} diff --git a/pkg/clinical/application/dto/output.go b/pkg/clinical/application/dto/output.go index 0ce0c536..9704093f 100644 --- a/pkg/clinical/application/dto/output.go +++ b/pkg/clinical/application/dto/output.go @@ -100,3 +100,12 @@ type MedicalData struct { ViralLoad []*Observation CD4Count []*Observation } + +type Patient struct { + ID string `json:"id"` + Active bool `json:"active"` + Name string `json:"name"` + PhoneNumber []string `json:"phoneNumber"` + Gender Gender `json:"gender"` + BirthDate scalarutils.Date `json:"birthDate"` +} diff --git a/pkg/clinical/application/utils/helpers.go b/pkg/clinical/application/utils/helpers.go index 7d911e10..50963554 100644 --- a/pkg/clinical/application/utils/helpers.go +++ b/pkg/clinical/application/utils/helpers.go @@ -23,9 +23,6 @@ const ( // OrganizationIDContextKey is the key used to add an organizationID to the context OrganizationIDContextKey = ContextKey("OrganizationID") - // ProgramIDContextKey is the key used to add a program to the context - ProgramIDContextKey = ContextKey("ProgramID") - // FacilityIDContextKey is the key used to add a facility to the context FacilityIDContextKey = ContextKey("FacilityID") ) diff --git a/pkg/clinical/domain/models.go b/pkg/clinical/domain/models.go index d0c08858..45b2eb66 100644 --- a/pkg/clinical/domain/models.go +++ b/pkg/clinical/domain/models.go @@ -199,9 +199,7 @@ func (p FHIRPatient) IsEntity() {} // PatientPayload is used to return patient records and ancillary data after // mutations. type PatientPayload struct { - PatientRecord *FHIRPatient `json:"patientRecord,omitempty"` - HasOpenEpisodes bool `json:"hasOpenEpisodes,omitempty"` - OpenEpisodes []*FHIREpisodeOfCare `json:"openEpisodes,omitempty"` + PatientRecord *FHIRPatient `json:"patientRecord,omitempty"` } // RetirePatientInput is used to retire patient records. diff --git a/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhir.go b/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhir.go index 4983bd5d..5f6b18f3 100644 --- a/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhir.go +++ b/pkg/clinical/infrastructure/datastore/cloudhealthcare/fhir.go @@ -1339,9 +1339,6 @@ func (fh *StoreImpl) CreateFHIRPatient(_ context.Context, input domain.FHIRPatie output := &domain.PatientPayload{ PatientRecord: resource, - // The patient is newly created so we can assume they have no open episodes - HasOpenEpisodes: false, - OpenEpisodes: []*domain.FHIREpisodeOfCare{}, } return output, nil diff --git a/pkg/clinical/infrastructure/datastore/cloudhealthcare/mock/fhir_mock.go b/pkg/clinical/infrastructure/datastore/cloudhealthcare/mock/fhir_mock.go index 8187884f..f3d2d296 100644 --- a/pkg/clinical/infrastructure/datastore/cloudhealthcare/mock/fhir_mock.go +++ b/pkg/clinical/infrastructure/datastore/cloudhealthcare/mock/fhir_mock.go @@ -567,13 +567,19 @@ func NewFHIRMock() *FHIRMock { return &domain.FHIRMedicationRelayPayload{}, nil }, MockCreateFHIRPatientFn: func(ctx context.Context, input domain.FHIRPatientInput) (*domain.PatientPayload, error) { + male := domain.PatientGenderEnumMale return &domain.PatientPayload{ PatientRecord: &domain.FHIRPatient{ - ID: new(string), - Text: &domain.FHIRNarrative{}, - Identifier: []*domain.FHIRIdentifier{}, - Active: new(bool), - Name: []*domain.FHIRHumanName{}, + ID: new(string), + Text: &domain.FHIRNarrative{}, + Gender: &male, + Identifier: []*domain.FHIRIdentifier{}, + Active: new(bool), + Name: []*domain.FHIRHumanName{ + { + Text: gofakeit.Name(), + }, + }, Telecom: []*domain.FHIRContactPoint{}, BirthDate: &scalarutils.Date{}, DeceasedBoolean: new(bool), @@ -589,8 +595,6 @@ func NewFHIRMock() *FHIRMock { ManagingOrganization: &domain.FHIRReference{}, Link: []*domain.FHIRPatientLink{}, }, - HasOpenEpisodes: false, - OpenEpisodes: []*domain.FHIREpisodeOfCare{}, }, nil }, MockPatchFHIRPatientFn: func(ctx context.Context, id string, params []map[string]interface{}) (*domain.FHIRPatient, error) { diff --git a/pkg/clinical/presentation/config.go b/pkg/clinical/presentation/config.go index e308dcb8..30d3525a 100644 --- a/pkg/clinical/presentation/config.go +++ b/pkg/clinical/presentation/config.go @@ -44,22 +44,6 @@ var ClinicalAllowedOrigins = []string{ "https://clinical-prod.bewell.co.ke", } -// ClinicalAllowedHeaders is a list of CORS allowed headers for the clinical -// service -var ClinicalAllowedHeaders = []string{ - "Accept", - "Accept-Charset", - "Accept-Language", - "Accept-Encoding", - "Origin", - "Host", - "User-Agent", - "Content-Length", - "Content-Type", - "Authorization", - "X-Authorization", -} - var ( authServerEndpoint = serverutils.MustGetEnvVar("AUTHSERVER_ENDPOINT") clientID = serverutils.MustGetEnvVar("CLIENT_ID") diff --git a/pkg/clinical/presentation/graph/clinical.graphql b/pkg/clinical/presentation/graph/clinical.graphql index d8210e22..397fa8d6 100644 --- a/pkg/clinical/presentation/graph/clinical.graphql +++ b/pkg/clinical/presentation/graph/clinical.graphql @@ -29,4 +29,8 @@ extend type Mutation { recordPulseRate(input: ObservationInput!): Observation! recordBloodPressure(input: ObservationInput!): Observation! recordBMI(input: ObservationInput!): Observation! + + # Patient + createPatient(input: PatientInput!): Patient! + } diff --git a/pkg/clinical/presentation/graph/clinical.resolvers.go b/pkg/clinical/presentation/graph/clinical.resolvers.go index 7d6b599d..6704c130 100644 --- a/pkg/clinical/presentation/graph/clinical.resolvers.go +++ b/pkg/clinical/presentation/graph/clinical.resolvers.go @@ -86,6 +86,13 @@ func (r *mutationResolver) RecordBmi(ctx context.Context, input dto.ObservationI return r.usecases.Clinical.RecordBMI(ctx, input) } +// CreatePatient is the resolver for the createPatient field. +func (r *mutationResolver) CreatePatient(ctx context.Context, input dto.PatientInput) (*dto.Patient, error) { + r.CheckDependencies() + + return r.usecases.CreatePatient(ctx, input) +} + // PatientHealthTimeline is the resolver for the patientHealthTimeline field. func (r *queryResolver) PatientHealthTimeline(ctx context.Context, input dto.HealthTimelineInput) (*dto.HealthTimeline, error) { r.CheckDependencies() diff --git a/pkg/clinical/presentation/graph/enums.graphql b/pkg/clinical/presentation/graph/enums.graphql index 38e3b37b..d1c286c1 100644 --- a/pkg/clinical/presentation/graph/enums.graphql +++ b/pkg/clinical/presentation/graph/enums.graphql @@ -43,4 +43,21 @@ enum MedicationStatementStatusEnum { active inactive unknown +} + +enum Gender { + male + female + other +} + +enum IdentifierType { + NATIONAL_ID + PASSPORT + ALIEN_ID + CCC_NUMBER +} + +enum ContactType { + PHONE_NUMBER } \ No newline at end of file diff --git a/pkg/clinical/presentation/graph/generated/generated.go b/pkg/clinical/presentation/graph/generated/generated.go index 3587d73b..ebb2eaae 100644 --- a/pkg/clinical/presentation/graph/generated/generated.go +++ b/pkg/clinical/presentation/graph/generated/generated.go @@ -332,6 +332,7 @@ type ComplexityRoot struct { Mutation struct { CreateEpisodeOfCare func(childComplexity int, episodeOfCare dto.EpisodeOfCareInput) int CreateFHIROrganization func(childComplexity int, input domain.FHIROrganizationInput) int + CreatePatient func(childComplexity int, input dto.PatientInput) int EndEncounter func(childComplexity int, encounterID string) int EndEpisodeOfCare func(childComplexity int, id string) int RecordBloodPressure func(childComplexity int, input dto.ObservationInput) int @@ -358,6 +359,15 @@ type ComplexityRoot struct { HasPreviousPage func(childComplexity int) int } + Patient struct { + Active func(childComplexity int) int + BirthDate func(childComplexity int) int + Gender func(childComplexity int) int + ID func(childComplexity int) int + Name func(childComplexity int) int + PhoneNumber func(childComplexity int) int + } + Query struct { EpisodeOfCare func(childComplexity int, id string) int GetMedicalData func(childComplexity int, patientID string) int @@ -393,6 +403,7 @@ type MutationResolver interface { RecordPulseRate(ctx context.Context, input dto.ObservationInput) (*dto.Observation, error) RecordBloodPressure(ctx context.Context, input dto.ObservationInput) (*dto.Observation, error) RecordBmi(ctx context.Context, input dto.ObservationInput) (*dto.Observation, error) + CreatePatient(ctx context.Context, input dto.PatientInput) (*dto.Patient, error) } type QueryResolver interface { PatientHealthTimeline(ctx context.Context, input dto.HealthTimelineInput) (*dto.HealthTimeline, error) @@ -1728,6 +1739,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Mutation.CreateFHIROrganization(childComplexity, args["input"].(domain.FHIROrganizationInput)), true + case "Mutation.createPatient": + if e.complexity.Mutation.CreatePatient == nil { + break + } + + args, err := ec.field_Mutation_createPatient_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.CreatePatient(childComplexity, args["input"].(dto.PatientInput)), true + case "Mutation.endEncounter": if e.complexity.Mutation.EndEncounter == nil { break @@ -1904,6 +1927,48 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.PageInfo.HasPreviousPage(childComplexity), true + case "Patient.active": + if e.complexity.Patient.Active == nil { + break + } + + return e.complexity.Patient.Active(childComplexity), true + + case "Patient.birthDate": + if e.complexity.Patient.BirthDate == nil { + break + } + + return e.complexity.Patient.BirthDate(childComplexity), true + + case "Patient.gender": + if e.complexity.Patient.Gender == nil { + break + } + + return e.complexity.Patient.Gender(childComplexity), true + + case "Patient.id": + if e.complexity.Patient.ID == nil { + break + } + + return e.complexity.Patient.ID(childComplexity), true + + case "Patient.name": + if e.complexity.Patient.Name == nil { + break + } + + return e.complexity.Patient.Name(childComplexity), true + + case "Patient.phoneNumber": + if e.complexity.Patient.PhoneNumber == nil { + break + } + + return e.complexity.Patient.PhoneNumber(childComplexity), true + case "Query.episodeOfCare": if e.complexity.Query.EpisodeOfCare == nil { break @@ -2016,6 +2081,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { rc := graphql.GetOperationContext(ctx) ec := executionContext{rc, e} inputUnmarshalMap := graphql.BuildUnmarshalerMap( + ec.unmarshalInputContactInput, ec.unmarshalInputEpisodeOfCareInput, ec.unmarshalInputFHIRAddressInput, ec.unmarshalInputFHIRAgeInput, @@ -2040,7 +2106,9 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ec.unmarshalInputFHIRTimingInput, ec.unmarshalInputFHIRTimingRepeatInput, ec.unmarshalInputHealthTimelineInput, + ec.unmarshalInputIdentifierInput, ec.unmarshalInputObservationInput, + ec.unmarshalInputPatientInput, ) first := true @@ -2132,6 +2200,10 @@ extend type Mutation { recordPulseRate(input: ObservationInput!): Observation! recordBloodPressure(input: ObservationInput!): Observation! recordBMI(input: ObservationInput!): Observation! + + # Patient + createPatient(input: PatientInput!): Patient! + } `, BuiltIn: false}, {Name: "../enums.graphql", Input: `enum EpisodeOfCareStatusEnum { @@ -2179,6 +2251,23 @@ enum MedicationStatementStatusEnum { active inactive unknown +} + +enum Gender { + male + female + other +} + +enum IdentifierType { + NATIONAL_ID + PASSPORT + ALIEN_ID + CCC_NUMBER +} + +enum ContactType { + PHONE_NUMBER }`, BuiltIn: false}, {Name: "../external.graphql", Input: `scalar Map scalar Any @@ -2221,7 +2310,26 @@ input ObservationInput { encounterID: String! value: String! } -`, BuiltIn: false}, + +input PatientInput { + firstName: String! + lastName: String + otherNames: String + birthDate: Date! + gender: Gender! + identifiers: [IdentifierInput!]! + contacts: [ContactInput!]! +} + +input IdentifierInput { + type: IdentifierType! + value: String! +} + +input ContactInput { + type: ContactType! + value: String! +}`, BuiltIn: false}, {Name: "../types.graphql", Input: `type AllergyIntolerance { id: ID! patientID: String! @@ -2293,7 +2401,14 @@ type Encounter { patientID: String } - +type Patient { + id: ID! + active: Boolean! + name: String! + phoneNumber: [String!]! + gender: Gender! + birthDate: Date +} `, BuiltIn: false}, {Name: "../fhir/Organization.graphql", Input: `""" FHIROrganizationInput: input for Organization @@ -4159,6 +4274,21 @@ func (ec *executionContext) field_Mutation_createFHIROrganization_args(ctx conte return args, nil } +func (ec *executionContext) field_Mutation_createPatient_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 dto.PatientInput + if tmp, ok := rawArgs["input"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("input")) + arg0, err = ec.unmarshalNPatientInput2githubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐPatientInput(ctx, tmp) + if err != nil { + return nil, err + } + } + args["input"] = arg0 + return args, nil +} + func (ec *executionContext) field_Mutation_endEncounter_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -13450,6 +13580,75 @@ func (ec *executionContext) fieldContext_Mutation_recordBMI(ctx context.Context, return fc, nil } +func (ec *executionContext) _Mutation_createPatient(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mutation_createPatient(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().CreatePatient(rctx, fc.Args["input"].(dto.PatientInput)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*dto.Patient) + fc.Result = res + return ec.marshalNPatient2ᚖgithubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐPatient(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Mutation_createPatient(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_Patient_id(ctx, field) + case "active": + return ec.fieldContext_Patient_active(ctx, field) + case "name": + return ec.fieldContext_Patient_name(ctx, field) + case "phoneNumber": + return ec.fieldContext_Patient_phoneNumber(ctx, field) + case "gender": + return ec.fieldContext_Patient_gender(ctx, field) + case "birthDate": + return ec.fieldContext_Patient_birthDate(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Patient", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Mutation_createPatient_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return + } + return fc, nil +} + func (ec *executionContext) _Observation_id(ctx context.Context, field graphql.CollectedField, obj *dto.Observation) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Observation_id(ctx, field) if err != nil { @@ -13802,8 +14001,8 @@ func (ec *executionContext) fieldContext_PageInfo_hasPreviousPage(ctx context.Co return fc, nil } -func (ec *executionContext) _Query_patientHealthTimeline(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query_patientHealthTimeline(ctx, field) +func (ec *executionContext) _Patient_id(ctx context.Context, field graphql.CollectedField, obj *dto.Patient) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Patient_id(ctx, field) if err != nil { return graphql.Null } @@ -13816,7 +14015,7 @@ func (ec *executionContext) _Query_patientHealthTimeline(ctx context.Context, fi }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().PatientHealthTimeline(rctx, fc.Args["input"].(dto.HealthTimelineInput)) + return obj.ID, nil }) if err != nil { ec.Error(ctx, err) @@ -13828,43 +14027,26 @@ func (ec *executionContext) _Query_patientHealthTimeline(ctx context.Context, fi } return graphql.Null } - res := resTmp.(*dto.HealthTimeline) + res := resTmp.(string) fc.Result = res - return ec.marshalNHealthTimeline2ᚖgithubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐHealthTimeline(ctx, field.Selections, res) + return ec.marshalNID2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_patientHealthTimeline(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Patient_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Query", + Object: "Patient", Field: field, - IsMethod: true, - IsResolver: true, + IsMethod: false, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "timeline": - return ec.fieldContext_HealthTimeline_timeline(ctx, field) - case "totalCount": - return ec.fieldContext_HealthTimeline_totalCount(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type HealthTimeline", field.Name) + return nil, errors.New("field of type ID does not have child fields") }, } - defer func() { - if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) - } - }() - ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Query_patientHealthTimeline_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { - ec.Error(ctx, err) - return - } return fc, nil } -func (ec *executionContext) _Query_getMedicalData(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query_getMedicalData(ctx, field) +func (ec *executionContext) _Patient_active(ctx context.Context, field graphql.CollectedField, obj *dto.Patient) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Patient_active(ctx, field) if err != nil { return graphql.Null } @@ -13877,60 +14059,38 @@ func (ec *executionContext) _Query_getMedicalData(ctx context.Context, field gra }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().GetMedicalData(rctx, fc.Args["patientID"].(string)) + return obj.Active, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*dto.MedicalData) + res := resTmp.(bool) fc.Result = res - return ec.marshalOMedicalData2ᚖgithubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐMedicalData(ctx, field.Selections, res) + return ec.marshalNBoolean2bool(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_getMedicalData(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Patient_active(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Query", + Object: "Patient", Field: field, - IsMethod: true, - IsResolver: true, + IsMethod: false, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "regimen": - return ec.fieldContext_MedicalData_regimen(ctx, field) - case "allergies": - return ec.fieldContext_MedicalData_allergies(ctx, field) - case "weight": - return ec.fieldContext_MedicalData_weight(ctx, field) - case "bmi": - return ec.fieldContext_MedicalData_bmi(ctx, field) - case "viralLoad": - return ec.fieldContext_MedicalData_viralLoad(ctx, field) - case "cd4Count": - return ec.fieldContext_MedicalData_cd4Count(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type MedicalData", field.Name) + return nil, errors.New("field of type Boolean does not have child fields") }, } - defer func() { - if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) - } - }() - ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Query_getMedicalData_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { - ec.Error(ctx, err) - return - } return fc, nil } -func (ec *executionContext) _Query_episodeOfCare(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query_episodeOfCare(ctx, field) +func (ec *executionContext) _Patient_name(ctx context.Context, field graphql.CollectedField, obj *dto.Patient) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Patient_name(ctx, field) if err != nil { return graphql.Null } @@ -13943,54 +14103,38 @@ func (ec *executionContext) _Query_episodeOfCare(ctx context.Context, field grap }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().EpisodeOfCare(rctx, fc.Args["id"].(string)) + return obj.Name, nil }) if err != nil { ec.Error(ctx, err) return graphql.Null } if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } return graphql.Null } - res := resTmp.(*dto.EpisodeOfCare) + res := resTmp.(string) fc.Result = res - return ec.marshalOEpisodeOfCare2ᚖgithubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐEpisodeOfCare(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_episodeOfCare(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Patient_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Query", + Object: "Patient", Field: field, - IsMethod: true, - IsResolver: true, + IsMethod: false, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "id": - return ec.fieldContext_EpisodeOfCare_id(ctx, field) - case "status": - return ec.fieldContext_EpisodeOfCare_status(ctx, field) - case "patientID": - return ec.fieldContext_EpisodeOfCare_patientID(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type EpisodeOfCare", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } - defer func() { - if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) - } - }() - ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Query_episodeOfCare_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { - ec.Error(ctx, err) - return - } return fc, nil } -func (ec *executionContext) _Query_listPatientEncounters(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query_listPatientEncounters(ctx, field) +func (ec *executionContext) _Patient_phoneNumber(ctx context.Context, field graphql.CollectedField, obj *dto.Patient) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Patient_phoneNumber(ctx, field) if err != nil { return graphql.Null } @@ -14003,7 +14147,7 @@ func (ec *executionContext) _Query_listPatientEncounters(ctx context.Context, fi }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().ListPatientEncounters(rctx, fc.Args["patientID"].(string)) + return obj.PhoneNumber, nil }) if err != nil { ec.Error(ctx, err) @@ -14015,27 +14159,343 @@ func (ec *executionContext) _Query_listPatientEncounters(ctx context.Context, fi } return graphql.Null } - res := resTmp.([]*dto.Encounter) + res := resTmp.([]string) fc.Result = res - return ec.marshalNEncounter2ᚕᚖgithubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐEncounterᚄ(ctx, field.Selections, res) + return ec.marshalNString2ᚕstringᚄ(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_listPatientEncounters(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Patient_phoneNumber(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Query", + Object: "Patient", Field: field, - IsMethod: true, - IsResolver: true, + IsMethod: false, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "id": - return ec.fieldContext_Encounter_id(ctx, field) - case "class": - return ec.fieldContext_Encounter_class(ctx, field) - case "episodeOfCareID": - return ec.fieldContext_Encounter_episodeOfCareID(ctx, field) - case "status": - return ec.fieldContext_Encounter_status(ctx, field) + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Patient_gender(ctx context.Context, field graphql.CollectedField, obj *dto.Patient) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Patient_gender(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Gender, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(dto.Gender) + fc.Result = res + return ec.marshalNGender2githubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐGender(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Patient_gender(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Patient", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Gender does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Patient_birthDate(ctx context.Context, field graphql.CollectedField, obj *dto.Patient) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Patient_birthDate(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.BirthDate, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(scalarutils.Date) + fc.Result = res + return ec.marshalODate2githubᚗcomᚋsavannahghiᚋscalarutilsᚐDate(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Patient_birthDate(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Patient", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Date does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_patientHealthTimeline(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_patientHealthTimeline(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().PatientHealthTimeline(rctx, fc.Args["input"].(dto.HealthTimelineInput)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*dto.HealthTimeline) + fc.Result = res + return ec.marshalNHealthTimeline2ᚖgithubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐHealthTimeline(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_patientHealthTimeline(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "timeline": + return ec.fieldContext_HealthTimeline_timeline(ctx, field) + case "totalCount": + return ec.fieldContext_HealthTimeline_totalCount(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type HealthTimeline", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_patientHealthTimeline_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return + } + return fc, nil +} + +func (ec *executionContext) _Query_getMedicalData(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_getMedicalData(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().GetMedicalData(rctx, fc.Args["patientID"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*dto.MedicalData) + fc.Result = res + return ec.marshalOMedicalData2ᚖgithubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐMedicalData(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_getMedicalData(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "regimen": + return ec.fieldContext_MedicalData_regimen(ctx, field) + case "allergies": + return ec.fieldContext_MedicalData_allergies(ctx, field) + case "weight": + return ec.fieldContext_MedicalData_weight(ctx, field) + case "bmi": + return ec.fieldContext_MedicalData_bmi(ctx, field) + case "viralLoad": + return ec.fieldContext_MedicalData_viralLoad(ctx, field) + case "cd4Count": + return ec.fieldContext_MedicalData_cd4Count(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type MedicalData", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_getMedicalData_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return + } + return fc, nil +} + +func (ec *executionContext) _Query_episodeOfCare(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_episodeOfCare(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().EpisodeOfCare(rctx, fc.Args["id"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*dto.EpisodeOfCare) + fc.Result = res + return ec.marshalOEpisodeOfCare2ᚖgithubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐEpisodeOfCare(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_episodeOfCare(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_EpisodeOfCare_id(ctx, field) + case "status": + return ec.fieldContext_EpisodeOfCare_status(ctx, field) + case "patientID": + return ec.fieldContext_EpisodeOfCare_patientID(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type EpisodeOfCare", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_episodeOfCare_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return + } + return fc, nil +} + +func (ec *executionContext) _Query_listPatientEncounters(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_listPatientEncounters(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().ListPatientEncounters(rctx, fc.Args["patientID"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*dto.Encounter) + fc.Result = res + return ec.marshalNEncounter2ᚕᚖgithubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐEncounterᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_listPatientEncounters(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_Encounter_id(ctx, field) + case "class": + return ec.fieldContext_Encounter_class(ctx, field) + case "episodeOfCareID": + return ec.fieldContext_Encounter_episodeOfCareID(ctx, field) + case "status": + return ec.fieldContext_Encounter_status(ctx, field) case "patientID": return ec.fieldContext_Encounter_patientID(ctx, field) } @@ -16279,22 +16739,58 @@ func (ec *executionContext) ___Type_specifiedByURL(ctx context.Context, field gr return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext___Type_specifiedByURL(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "__Type", - Field: field, - IsMethod: true, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, +func (ec *executionContext) fieldContext___Type_specifiedByURL(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Type", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +// endregion **************************** field.gotpl ***************************** + +// region **************************** input.gotpl ***************************** + +func (ec *executionContext) unmarshalInputContactInput(ctx context.Context, obj interface{}) (dto.ContactInput, error) { + var it dto.ContactInput + asMap := map[string]interface{}{} + for k, v := range obj.(map[string]interface{}) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"type", "value"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "type": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("type")) + it.Type, err = ec.unmarshalNContactType2githubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐContactType(ctx, v) + if err != nil { + return it, err + } + case "value": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("value")) + it.Value, err = ec.unmarshalNString2string(ctx, v) + if err != nil { + return it, err + } + } } - return fc, nil -} - -// endregion **************************** field.gotpl ***************************** -// region **************************** input.gotpl ***************************** + return it, nil +} func (ec *executionContext) unmarshalInputEpisodeOfCareInput(ctx context.Context, obj interface{}) (dto.EpisodeOfCareInput, error) { var it dto.EpisodeOfCareInput @@ -18016,6 +18512,42 @@ func (ec *executionContext) unmarshalInputHealthTimelineInput(ctx context.Contex return it, nil } +func (ec *executionContext) unmarshalInputIdentifierInput(ctx context.Context, obj interface{}) (dto.IdentifierInput, error) { + var it dto.IdentifierInput + asMap := map[string]interface{}{} + for k, v := range obj.(map[string]interface{}) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"type", "value"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "type": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("type")) + it.Type, err = ec.unmarshalNIdentifierType2githubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐIdentifierType(ctx, v) + if err != nil { + return it, err + } + case "value": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("value")) + it.Value, err = ec.unmarshalNString2string(ctx, v) + if err != nil { + return it, err + } + } + } + + return it, nil +} + func (ec *executionContext) unmarshalInputObservationInput(ctx context.Context, obj interface{}) (dto.ObservationInput, error) { var it dto.ObservationInput asMap := map[string]interface{}{} @@ -18060,6 +18592,82 @@ func (ec *executionContext) unmarshalInputObservationInput(ctx context.Context, return it, nil } +func (ec *executionContext) unmarshalInputPatientInput(ctx context.Context, obj interface{}) (dto.PatientInput, error) { + var it dto.PatientInput + asMap := map[string]interface{}{} + for k, v := range obj.(map[string]interface{}) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"firstName", "lastName", "otherNames", "birthDate", "gender", "identifiers", "contacts"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "firstName": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("firstName")) + it.FirstName, err = ec.unmarshalNString2string(ctx, v) + if err != nil { + return it, err + } + case "lastName": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("lastName")) + it.LastName, err = ec.unmarshalOString2string(ctx, v) + if err != nil { + return it, err + } + case "otherNames": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("otherNames")) + it.OtherNames, err = ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + case "birthDate": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("birthDate")) + it.BirthDate, err = ec.unmarshalNDate2githubᚗcomᚋsavannahghiᚋscalarutilsᚐDate(ctx, v) + if err != nil { + return it, err + } + case "gender": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("gender")) + it.Gender, err = ec.unmarshalNGender2githubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐGender(ctx, v) + if err != nil { + return it, err + } + case "identifiers": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("identifiers")) + it.Identifiers, err = ec.unmarshalNIdentifierInput2ᚕgithubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐIdentifierInputᚄ(ctx, v) + if err != nil { + return it, err + } + case "contacts": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("contacts")) + it.Contacts, err = ec.unmarshalNContactInput2ᚕgithubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐContactInputᚄ(ctx, v) + if err != nil { + return it, err + } + } + } + + return it, nil +} + // endregion **************************** input.gotpl ***************************** // region ************************** interface.gotpl *************************** @@ -19708,6 +20316,15 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) return ec._Mutation_recordBMI(ctx, field) }) + if out.Values[i] == graphql.Null { + invalids++ + } + case "createPatient": + + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_createPatient(ctx, field) + }) + if out.Values[i] == graphql.Null { invalids++ } @@ -19820,6 +20437,66 @@ func (ec *executionContext) _PageInfo(ctx context.Context, sel ast.SelectionSet, return out } +var patientImplementors = []string{"Patient"} + +func (ec *executionContext) _Patient(ctx context.Context, sel ast.SelectionSet, obj *dto.Patient) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, patientImplementors) + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Patient") + case "id": + + out.Values[i] = ec._Patient_id(ctx, field, obj) + + if out.Values[i] == graphql.Null { + invalids++ + } + case "active": + + out.Values[i] = ec._Patient_active(ctx, field, obj) + + if out.Values[i] == graphql.Null { + invalids++ + } + case "name": + + out.Values[i] = ec._Patient_name(ctx, field, obj) + + if out.Values[i] == graphql.Null { + invalids++ + } + case "phoneNumber": + + out.Values[i] = ec._Patient_phoneNumber(ctx, field, obj) + + if out.Values[i] == graphql.Null { + invalids++ + } + case "gender": + + out.Values[i] = ec._Patient_gender(ctx, field, obj) + + if out.Values[i] == graphql.Null { + invalids++ + } + case "birthDate": + + out.Values[i] = ec._Patient_birthDate(ctx, field, obj) + + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + var queryImplementors = []string{"Query"} func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) graphql.Marshaler { @@ -20387,6 +21064,54 @@ func (ec *executionContext) marshalNCode2githubᚗcomᚋsavannahghiᚋscalarutil return v } +func (ec *executionContext) unmarshalNContactInput2githubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐContactInput(ctx context.Context, v interface{}) (dto.ContactInput, error) { + res, err := ec.unmarshalInputContactInput(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) unmarshalNContactInput2ᚕgithubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐContactInputᚄ(ctx context.Context, v interface{}) ([]dto.ContactInput, error) { + var vSlice []interface{} + if v != nil { + vSlice = graphql.CoerceList(v) + } + var err error + res := make([]dto.ContactInput, len(vSlice)) + for i := range vSlice { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i)) + res[i], err = ec.unmarshalNContactInput2githubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐContactInput(ctx, vSlice[i]) + if err != nil { + return nil, err + } + } + return res, nil +} + +func (ec *executionContext) unmarshalNContactType2githubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐContactType(ctx context.Context, v interface{}) (dto.ContactType, error) { + tmp, err := graphql.UnmarshalString(v) + res := dto.ContactType(tmp) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNContactType2githubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐContactType(ctx context.Context, sel ast.SelectionSet, v dto.ContactType) graphql.Marshaler { + res := graphql.MarshalString(string(v)) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + } + return res +} + +func (ec *executionContext) unmarshalNDate2githubᚗcomᚋsavannahghiᚋscalarutilsᚐDate(ctx context.Context, v interface{}) (scalarutils.Date, error) { + var res scalarutils.Date + err := res.UnmarshalGQL(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNDate2githubᚗcomᚋsavannahghiᚋscalarutilsᚐDate(ctx context.Context, sel ast.SelectionSet, v scalarutils.Date) graphql.Marshaler { + return v +} + func (ec *executionContext) unmarshalNDateTime2githubᚗcomᚋsavannahghiᚋscalarutilsᚐDateTime(ctx context.Context, v interface{}) (scalarutils.DateTime, error) { var res scalarutils.DateTime err := res.UnmarshalGQL(v) @@ -20625,6 +21350,22 @@ func (ec *executionContext) marshalNFloat2float64(ctx context.Context, sel ast.S return graphql.WrapContextMarshaler(ctx, res) } +func (ec *executionContext) unmarshalNGender2githubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐGender(ctx context.Context, v interface{}) (dto.Gender, error) { + tmp, err := graphql.UnmarshalString(v) + res := dto.Gender(tmp) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNGender2githubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐGender(ctx context.Context, sel ast.SelectionSet, v dto.Gender) graphql.Marshaler { + res := graphql.MarshalString(string(v)) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + } + return res +} + func (ec *executionContext) marshalNHealthTimeline2githubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐHealthTimeline(ctx context.Context, sel ast.SelectionSet, v dto.HealthTimeline) graphql.Marshaler { return ec._HealthTimeline(ctx, sel, &v) } @@ -20669,6 +21410,44 @@ func (ec *executionContext) marshalNID2string(ctx context.Context, sel ast.Selec return res } +func (ec *executionContext) unmarshalNIdentifierInput2githubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐIdentifierInput(ctx context.Context, v interface{}) (dto.IdentifierInput, error) { + res, err := ec.unmarshalInputIdentifierInput(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) unmarshalNIdentifierInput2ᚕgithubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐIdentifierInputᚄ(ctx context.Context, v interface{}) ([]dto.IdentifierInput, error) { + var vSlice []interface{} + if v != nil { + vSlice = graphql.CoerceList(v) + } + var err error + res := make([]dto.IdentifierInput, len(vSlice)) + for i := range vSlice { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i)) + res[i], err = ec.unmarshalNIdentifierInput2githubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐIdentifierInput(ctx, vSlice[i]) + if err != nil { + return nil, err + } + } + return res, nil +} + +func (ec *executionContext) unmarshalNIdentifierType2githubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐIdentifierType(ctx context.Context, v interface{}) (dto.IdentifierType, error) { + tmp, err := graphql.UnmarshalString(v) + res := dto.IdentifierType(tmp) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNIdentifierType2githubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐIdentifierType(ctx context.Context, sel ast.SelectionSet, v dto.IdentifierType) graphql.Marshaler { + res := graphql.MarshalString(string(v)) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + } + return res +} + func (ec *executionContext) unmarshalNIdentifierUseEnum2githubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋdomainᚐIdentifierUseEnum(ctx context.Context, v interface{}) (domain.IdentifierUseEnum, error) { var res domain.IdentifierUseEnum err := res.UnmarshalGQL(v) @@ -20743,6 +21522,25 @@ func (ec *executionContext) marshalNPageInfo2ᚖgithubᚗcomᚋsavannahghiᚋfir return ec._PageInfo(ctx, sel, v) } +func (ec *executionContext) marshalNPatient2githubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐPatient(ctx context.Context, sel ast.SelectionSet, v dto.Patient) graphql.Marshaler { + return ec._Patient(ctx, sel, &v) +} + +func (ec *executionContext) marshalNPatient2ᚖgithubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐPatient(ctx context.Context, sel ast.SelectionSet, v *dto.Patient) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._Patient(ctx, sel, v) +} + +func (ec *executionContext) unmarshalNPatientInput2githubᚗcomᚋsavannahghiᚋclinicalᚋpkgᚋclinicalᚋapplicationᚋdtoᚐPatientInput(ctx context.Context, v interface{}) (dto.PatientInput, error) { + res, err := ec.unmarshalInputPatientInput(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + func (ec *executionContext) unmarshalNString2string(ctx context.Context, v interface{}) (string, error) { res, err := graphql.UnmarshalString(v) return res, graphql.ErrorOnPath(ctx, err) diff --git a/pkg/clinical/presentation/graph/inputs.graphql b/pkg/clinical/presentation/graph/inputs.graphql index 1a01b503..73585e66 100644 --- a/pkg/clinical/presentation/graph/inputs.graphql +++ b/pkg/clinical/presentation/graph/inputs.graphql @@ -14,3 +14,23 @@ input ObservationInput { encounterID: String! value: String! } + +input PatientInput { + firstName: String! + lastName: String + otherNames: String + birthDate: Date! + gender: Gender! + identifiers: [IdentifierInput!]! + contacts: [ContactInput!]! +} + +input IdentifierInput { + type: IdentifierType! + value: String! +} + +input ContactInput { + type: ContactType! + value: String! +} \ No newline at end of file diff --git a/pkg/clinical/presentation/graph/types.graphql b/pkg/clinical/presentation/graph/types.graphql index 697d0422..6b5c9366 100644 --- a/pkg/clinical/presentation/graph/types.graphql +++ b/pkg/clinical/presentation/graph/types.graphql @@ -69,4 +69,11 @@ type Encounter { patientID: String } - +type Patient { + id: ID! + active: Boolean! + name: String! + phoneNumber: [String!]! + gender: Gender! + birthDate: Date +} diff --git a/pkg/clinical/usecases/clinical/patient.go b/pkg/clinical/usecases/clinical/patient.go index 9980e067..0385052d 100644 --- a/pkg/clinical/usecases/clinical/patient.go +++ b/pkg/clinical/usecases/clinical/patient.go @@ -4,6 +4,9 @@ import ( "context" "fmt" + "github.com/savannahghi/clinical/pkg/clinical/application/extensions" + "github.com/savannahghi/scalarutils" + "github.com/savannahghi/clinical/pkg/clinical/application/common" "github.com/savannahghi/clinical/pkg/clinical/application/dto" "github.com/savannahghi/clinical/pkg/clinical/application/utils" @@ -344,3 +347,113 @@ func (c *UseCasesClinicalImpl) CreateFHIROrganization(ctx context.Context, input return organizationRelayPayload, nil } + +// CreatePatient creates a new patient +func (c *UseCasesClinicalImpl) CreatePatient(ctx context.Context, input dto.PatientInput) (*dto.Patient, error) { + facilityID, err := extensions.GetFacilityIDFromContext(ctx) + if err != nil { + return nil, err + } + + facility, err := c.infrastructure.FHIR.GetFHIROrganization(ctx, facilityID) + if err != nil { + return nil, err + } + + nameInput := &domain.NameInput{ + FirstName: input.FirstName, + LastName: input.LastName, + OtherNames: input.OtherNames, + } + + phoneNumbers := []*domain.PhoneNumberInput{} + + for _, contact := range input.Contacts { + if contact.Type == dto.ContactTypePhoneNumber { + number := &domain.PhoneNumberInput{ + Msisdn: contact.Value, + } + phoneNumbers = append(phoneNumbers, number) + } + } + + documents := []*domain.IdentificationDocument{} + + for _, identifier := range input.Identifiers { + doc := &domain.IdentificationDocument{ + DocumentType: domain.IDDocumentType(identifier.Type), + DocumentNumber: identifier.Value, + } + + documents = append(documents, doc) + } + + registrationInput := domain.SimplePatientRegistrationInput{ + Names: []*domain.NameInput{nameInput}, + BirthDate: input.BirthDate, + PhoneNumbers: phoneNumbers, + Gender: string(input.Gender), + Active: true, + IdentificationDocuments: documents, + } + + exists, err := c.CheckPatientExistenceUsingPhoneNumber(ctx, registrationInput) + if err != nil { + utils.ReportErrorToSentry(err) + return nil, fmt.Errorf("unable to check patient existence: %w", err) + } + + if exists { + return nil, fmt.Errorf("patient with phone number already exists") + } + + patientInput, err := c.SimplePatientRegistrationInputToPatientInput(ctx, registrationInput) + if err != nil { + return nil, err + } + + orgRef := fmt.Sprintf("Organization/%s", *facility.Resource.ID) + orgType := scalarutils.URI("Organization") + + patientInput.ManagingOrganization = &domain.FHIRReferenceInput{ + ID: facility.Resource.ID, + Reference: &orgRef, + Display: *facility.Resource.Name, + Type: &orgType, + } + + tags, err := c.GetTenantMetaTags(ctx) + if err != nil { + return nil, err + } + + patientInput.Meta = domain.FHIRMetaInput{ + Tag: tags, + } + + patient, err := c.infrastructure.FHIR.CreateFHIRPatient(ctx, *patientInput) + if err != nil { + return nil, err + } + + return mapFHIRPatientToPatientDTO(patient.PatientRecord), nil +} + +func mapFHIRPatientToPatientDTO(patient *domain.FHIRPatient) *dto.Patient { + numbers := []string{} + + for _, phone := range patient.Telecom { + if *phone.System == domain.ContactPointSystemEnumPhone { + numbers = append(numbers, *phone.Value) + } + } + + return &dto.Patient{ + ID: *patient.ID, + Active: *patient.Active, + Name: patient.Name[0].Text, + PhoneNumber: numbers, + Gender: dto.Gender(patient.Gender.String()), + BirthDate: *patient.BirthDate, + } +} diff --git a/pkg/clinical/usecases/clinical/patient_unit_test.go b/pkg/clinical/usecases/clinical/patient_unit_test.go index 4c77b706..8516bb53 100644 --- a/pkg/clinical/usecases/clinical/patient_unit_test.go +++ b/pkg/clinical/usecases/clinical/patient_unit_test.go @@ -1256,3 +1256,312 @@ func TestClinicalUseCaseImpl_GetMedicalData(t *testing.T) { } } + +func TestUseCasesClinicalImpl_CreatePatient(t *testing.T) { + + type args struct { + ctx context.Context + input dto.PatientInput + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "happy case: register a patient", + args: args{ + ctx: addTenantIdentifierContext(context.Background()), + input: dto.PatientInput{ + FirstName: gofakeit.Name(), + LastName: gofakeit.Name(), + BirthDate: scalarutils.Date{ + Year: 1997, + Month: 12, + Day: 12, + }, + Gender: dto.GenderFemale, + Identifiers: []dto.IdentifierInput{ + { + Type: dto.IdentifierTypeNationalID, + Value: "12345678", + }, + }, + Contacts: []dto.ContactInput{ + { + Type: dto.ContactTypePhoneNumber, + Value: "0700000000", + }, + }, + }, + }, + wantErr: false, + }, + { + name: "sad case: patient already exists", + args: args{ + ctx: addTenantIdentifierContext(context.Background()), + input: dto.PatientInput{ + FirstName: gofakeit.Name(), + LastName: gofakeit.Name(), + BirthDate: scalarutils.Date{ + Year: 1997, + Month: 12, + Day: 12, + }, + Gender: dto.GenderFemale, + Identifiers: []dto.IdentifierInput{ + { + Type: dto.IdentifierTypeNationalID, + Value: "12345678", + }, + }, + Contacts: []dto.ContactInput{ + { + Type: dto.ContactTypePhoneNumber, + Value: "0700000000", + }, + }, + }, + }, + wantErr: true, + }, + { + name: "sad case: error searching for patient", + args: args{ + ctx: addTenantIdentifierContext(context.Background()), + input: dto.PatientInput{ + FirstName: gofakeit.Name(), + LastName: gofakeit.Name(), + BirthDate: scalarutils.Date{ + Year: 1997, + Month: 12, + Day: 12, + }, + Gender: dto.GenderFemale, + Identifiers: []dto.IdentifierInput{ + { + Type: dto.IdentifierTypeNationalID, + Value: "12345678", + }, + }, + Contacts: []dto.ContactInput{ + { + Type: dto.ContactTypePhoneNumber, + Value: "0700000000", + }, + }, + }, + }, + wantErr: true, + }, + { + name: "sad case: invalid phone number", + args: args{ + ctx: addTenantIdentifierContext(context.Background()), + input: dto.PatientInput{ + FirstName: gofakeit.Name(), + LastName: gofakeit.Name(), + BirthDate: scalarutils.Date{ + Year: 1997, + Month: 12, + Day: 12, + }, + Gender: dto.GenderFemale, + Identifiers: []dto.IdentifierInput{ + { + Type: dto.IdentifierTypeNationalID, + Value: "12345678", + }, + }, + Contacts: []dto.ContactInput{ + { + Type: dto.ContactTypePhoneNumber, + Value: "070000", + }, + }, + }, + }, + wantErr: true, + }, + { + name: "sad case: fail to get tenant tags", + args: args{ + ctx: addTenantIdentifierContext(context.Background()), + input: dto.PatientInput{ + FirstName: gofakeit.Name(), + LastName: gofakeit.Name(), + BirthDate: scalarutils.Date{ + Year: 1997, + Month: 12, + Day: 12, + }, + Gender: dto.GenderFemale, + Identifiers: []dto.IdentifierInput{ + { + Type: dto.IdentifierTypeNationalID, + Value: "12345678", + }, + }, + Contacts: []dto.ContactInput{ + { + Type: dto.ContactTypePhoneNumber, + Value: "0700000000", + }, + }, + }, + }, + wantErr: true, + }, + { + name: "sad case: fail to create patient", + args: args{ + ctx: addTenantIdentifierContext(context.Background()), + input: dto.PatientInput{ + FirstName: gofakeit.Name(), + LastName: gofakeit.Name(), + BirthDate: scalarutils.Date{ + Year: 1997, + Month: 12, + Day: 12, + }, + Gender: dto.GenderFemale, + Identifiers: []dto.IdentifierInput{ + { + Type: dto.IdentifierTypeNationalID, + Value: "12345678", + }, + }, + Contacts: []dto.ContactInput{ + { + Type: dto.ContactTypePhoneNumber, + Value: "0700000000", + }, + }, + }, + }, + wantErr: true, + }, + { + name: "sad case: no facility id in context", + args: args{ + ctx: context.Background(), + input: dto.PatientInput{ + FirstName: gofakeit.Name(), + LastName: gofakeit.Name(), + BirthDate: scalarutils.Date{ + Year: 1997, + Month: 12, + Day: 12, + }, + Gender: dto.GenderFemale, + Identifiers: []dto.IdentifierInput{ + { + Type: dto.IdentifierTypeNationalID, + Value: "12345678", + }, + }, + Contacts: []dto.ContactInput{ + { + Type: dto.ContactTypePhoneNumber, + Value: "0700000000", + }, + }, + }, + }, + wantErr: true, + }, + { + name: "sad case: fail to find facility", + args: args{ + ctx: addTenantIdentifierContext(context.Background()), + input: dto.PatientInput{ + FirstName: gofakeit.Name(), + LastName: gofakeit.Name(), + BirthDate: scalarutils.Date{ + Year: 1997, + Month: 12, + Day: 12, + }, + Gender: dto.GenderFemale, + Identifiers: []dto.IdentifierInput{ + { + Type: dto.IdentifierTypeNationalID, + Value: "12345678", + }, + }, + Contacts: []dto.ContactInput{ + { + Type: dto.ContactTypePhoneNumber, + Value: "0700000000", + }, + }, + }, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fakeExt := fakeExtMock.NewFakeBaseExtensionMock() + fakeFHIR := fakeFHIRMock.NewFHIRMock() + fakeOCL := fakeOCLMock.NewFakeOCLMock() + fakeMCH := fakeMyCarehubMock.NewFakeMyCareHubServiceMock() + + infra := infrastructure.NewInfrastructureInteractor(fakeExt, fakeFHIR, fakeOCL, fakeMCH) + c := clinicalUsecase.NewUseCasesClinicalImpl(infra) + + if tt.name == "sad case: patient already exists" { + fakeFHIR.MockSearchFHIRPatientFn = func(ctx context.Context, searchParams string, tenant dto.TenantIdentifiers) (*domain.PatientConnection, error) { + return &domain.PatientConnection{ + Edges: []*domain.PatientEdge{ + { + Node: &domain.FHIRPatient{}, + }, + }, + }, nil + } + } + + if tt.name == "sad case: error searching for patient" { + fakeFHIR.MockSearchFHIRPatientFn = func(ctx context.Context, searchParams string, tenant dto.TenantIdentifiers) (*domain.PatientConnection, error) { + return nil, fmt.Errorf("failed to search for patient") + } + } + + if tt.name == "sad case: fail to get tenant tags" { + fakeExt.MockGetTenantIdentifiersFn = func(ctx context.Context) (*dto.TenantIdentifiers, error) { + return nil, fmt.Errorf("failed to get tenant identifiers") + } + } + + if tt.name == "sad case: fail to create patient" { + fakeFHIR.MockCreateFHIRPatientFn = func(ctx context.Context, input domain.FHIRPatientInput) (*domain.PatientPayload, error) { + return nil, fmt.Errorf("failed to create patient") + } + } + + if tt.name == "sad case: fail to find facility" { + fakeFHIR.MockGetFHIROrganizationFn = func(ctx context.Context, organisationID string) (*domain.FHIROrganizationRelayPayload, error) { + return nil, fmt.Errorf("failed to find facility") + } + } + + got, err := c.CreatePatient(tt.args.ctx, tt.args.input) + if (err != nil) != tt.wantErr { + t.Errorf("CreatePatient() error = %v, wantErr %v", err, tt.wantErr) + return + } + + if tt.wantErr && got != nil { + t.Errorf("expected patient to be nil for %v", tt.name) + return + } + + if !tt.wantErr && got == nil { + t.Errorf("expected patients not to be nil for %v", tt.name) + return + } + }) + } +} diff --git a/pkg/clinical/usecases/clinical/utils.go b/pkg/clinical/usecases/clinical/utils.go index 0e5ecd75..5e05161f 100644 --- a/pkg/clinical/usecases/clinical/utils.go +++ b/pkg/clinical/usecases/clinical/utils.go @@ -80,7 +80,7 @@ func (c *UseCasesClinicalImpl) CheckPatientExistenceUsingPhoneNumber(ctx context return false, fmt.Errorf("unable to find patient by phonenumber: %s", *phoneNumber) } - if len(patient.Edges) > 1 { + if len(patient.Edges) >= 1 { exists = true break } diff --git a/pkg/clinical/usecases/usecases.go b/pkg/clinical/usecases/usecases.go index 5ff4a300..db10bd22 100644 --- a/pkg/clinical/usecases/usecases.go +++ b/pkg/clinical/usecases/usecases.go @@ -27,6 +27,8 @@ type Clinical interface { CreatePubsubTestResult(ctx context.Context, data dto.CreatePatientTestResultPubSubMessage) error CreatePubsubMedicationStatement(ctx context.Context, data dto.CreateMedicationPubSubMessage) error + CreatePatient(ctx context.Context, input dto.PatientInput) (*dto.Patient, error) + StartEncounter(ctx context.Context, episodeID string) (string, error) EndEncounter(ctx context.Context, encounterID string) (bool, error) ListPatientEncounters(ctx context.Context, patientID string) ([]*dto.Encounter, error)