From 0836ce99f2b0db81943cd775d5eb7d416ec7a5b9 Mon Sep 17 00:00:00 2001 From: Marwan Tammam Date: Thu, 30 Oct 2025 10:42:26 -0400 Subject: [PATCH] Fix issues with resolve and improve test coverage --- fhirpath/fhirpath_test.go | 9 +- fhirpath/internal/funcs/impl/resolve.go | 318 +----------------- fhirpath/internal/funcs/impl/resolve_test.go | 46 ++- .../resolver/resolvertest/resolvertest.go | 14 + 4 files changed, 69 insertions(+), 318 deletions(-) diff --git a/fhirpath/fhirpath_test.go b/fhirpath/fhirpath_test.go index 2a60de2..d3e3fd3 100644 --- a/fhirpath/fhirpath_test.go +++ b/fhirpath/fhirpath_test.go @@ -219,8 +219,9 @@ func testEvaluate(t *testing.T, testCases []evaluateTestCase) { func TestResolve(t *testing.T) { var patientChuRef = &dtpb.Reference{ - Type: fhir.URI("Patient"), - Id: fhir.String("123"), + Reference: &dtpb.Reference_Uri{ + Uri: &dtpb.String{Value: "Patient/123"}, + }, } var obsWithPatientChuRef = &opb.Observation{ @@ -240,7 +241,7 @@ func TestResolve(t *testing.T) { obsWithPatientChuRef, }, evaluateOptions: []fhirpath.EvaluateOption{ - evalopts.WithResolver(resolvertest.HappyResolver(patientChu))}, + evalopts.WithResolver(resolvertest.SimpleResolver(map[string][]fhirpath.Resource{"Patient/123": {patientChu}}))}, wantCollection: system.Collection{patientChu}, }, { @@ -281,7 +282,7 @@ func TestResolve(t *testing.T) { obsWithPatientTsuRef, }, evaluateOptions: []fhirpath.EvaluateOption{ - evalopts.WithResolver(resolvertest.HappyResolver(patientChu)), + evalopts.WithResolver(resolvertest.SimpleResolver(map[string][]fhirpath.Resource{"Patient/123": {patientChu}})), }, wantCollection: system.Collection{patientChu}, }, diff --git a/fhirpath/internal/funcs/impl/resolve.go b/fhirpath/internal/funcs/impl/resolve.go index 8d9b60e..f2bc10b 100644 --- a/fhirpath/internal/funcs/impl/resolve.go +++ b/fhirpath/internal/funcs/impl/resolve.go @@ -4,9 +4,11 @@ import ( "errors" "fmt" + "github.com/google/fhir/go/jsonformat" dtpb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/datatypes_go_proto" "github.com/verily-src/fhirpath-go/fhirpath/internal/expr" "github.com/verily-src/fhirpath-go/fhirpath/system" + "google.golang.org/protobuf/proto" ) var ( @@ -24,317 +26,14 @@ func isValidResolveInput(item any) bool { } func stringifyReference(ref *dtpb.Reference) string { - if ref == nil || ref.GetType() == nil || ref.GetReference() == nil { + if ref == nil { return "" } - refType := ref.GetType().GetValue() - - switch ref.GetReference().(type) { - case *dtpb.Reference_AccountId: - return fmt.Sprintf("%s/%s", refType, ref.GetAccountId().GetValue()) - case *dtpb.Reference_ActivityDefinitionId: - return fmt.Sprintf("%s/%s", refType, ref.GetActivityDefinitionId().GetValue()) - case *dtpb.Reference_AdverseEventId: - return fmt.Sprintf("%s/%s", refType, ref.GetAdverseEventId().GetValue()) - case *dtpb.Reference_AllergyIntoleranceId: - return fmt.Sprintf("%s/%s", refType, ref.GetAllergyIntoleranceId().GetValue()) - case *dtpb.Reference_AppointmentId: - return fmt.Sprintf("%s/%s", refType, ref.GetAppointmentId().GetValue()) - case *dtpb.Reference_AppointmentResponseId: - return fmt.Sprintf("%s/%s", refType, ref.GetAppointmentResponseId().GetValue()) - case *dtpb.Reference_AuditEventId: - return fmt.Sprintf("%s/%s", refType, ref.GetAuditEventId().GetValue()) - case *dtpb.Reference_BasicId: - return fmt.Sprintf("%s/%s", refType, ref.GetBasicId().GetValue()) - case *dtpb.Reference_BinaryId: - return fmt.Sprintf("%s/%s", refType, ref.GetBinaryId().GetValue()) - case *dtpb.Reference_BiologicallyDerivedProductId: - return fmt.Sprintf("%s/%s", refType, ref.GetBiologicallyDerivedProductId().GetValue()) - case *dtpb.Reference_BodyStructureId: - return fmt.Sprintf("%s/%s", refType, ref.GetBodyStructureId().GetValue()) - case *dtpb.Reference_BundleId: - return fmt.Sprintf("%s/%s", refType, ref.GetBundleId().GetValue()) - case *dtpb.Reference_CapabilityStatementId: - return fmt.Sprintf("%s/%s", refType, ref.GetCapabilityStatementId().GetValue()) - case *dtpb.Reference_CarePlanId: - return fmt.Sprintf("%s/%s", refType, ref.GetCarePlanId().GetValue()) - case *dtpb.Reference_CareTeamId: - return fmt.Sprintf("%s/%s", refType, ref.GetCareTeamId().GetValue()) - case *dtpb.Reference_CatalogEntryId: - return fmt.Sprintf("%s/%s", refType, ref.GetCatalogEntryId().GetValue()) - case *dtpb.Reference_ChargeItemId: - return fmt.Sprintf("%s/%s", refType, ref.GetChargeItemId().GetValue()) - case *dtpb.Reference_ChargeItemDefinitionId: - return fmt.Sprintf("%s/%s", refType, ref.GetChargeItemDefinitionId().GetValue()) - case *dtpb.Reference_ClaimId: - return fmt.Sprintf("%s/%s", refType, ref.GetClaimId().GetValue()) - case *dtpb.Reference_ClaimResponseId: - return fmt.Sprintf("%s/%s", refType, ref.GetClaimResponseId().GetValue()) - case *dtpb.Reference_ClinicalImpressionId: - return fmt.Sprintf("%s/%s", refType, ref.GetClinicalImpressionId().GetValue()) - case *dtpb.Reference_CodeSystemId: - return fmt.Sprintf("%s/%s", refType, ref.GetCodeSystemId().GetValue()) - case *dtpb.Reference_CommunicationId: - return fmt.Sprintf("%s/%s", refType, ref.GetCommunicationId().GetValue()) - case *dtpb.Reference_CommunicationRequestId: - return fmt.Sprintf("%s/%s", refType, ref.GetCommunicationRequestId().GetValue()) - case *dtpb.Reference_CompartmentDefinitionId: - return fmt.Sprintf("%s/%s", refType, ref.GetCompartmentDefinitionId().GetValue()) - case *dtpb.Reference_CompositionId: - return fmt.Sprintf("%s/%s", refType, ref.GetCompositionId().GetValue()) - case *dtpb.Reference_ConceptMapId: - return fmt.Sprintf("%s/%s", refType, ref.GetConceptMapId().GetValue()) - case *dtpb.Reference_ConditionId: - return fmt.Sprintf("%s/%s", refType, ref.GetConditionId().GetValue()) - case *dtpb.Reference_ConsentId: - return fmt.Sprintf("%s/%s", refType, ref.GetConsentId().GetValue()) - case *dtpb.Reference_ContractId: - return fmt.Sprintf("%s/%s", refType, ref.GetContractId().GetValue()) - case *dtpb.Reference_CoverageId: - return fmt.Sprintf("%s/%s", refType, ref.GetCoverageId().GetValue()) - case *dtpb.Reference_CoverageEligibilityRequestId: - return fmt.Sprintf("%s/%s", refType, ref.GetCoverageEligibilityRequestId().GetValue()) - case *dtpb.Reference_CoverageEligibilityResponseId: - return fmt.Sprintf("%s/%s", refType, ref.GetCoverageEligibilityResponseId().GetValue()) - case *dtpb.Reference_DetectedIssueId: - return fmt.Sprintf("%s/%s", refType, ref.GetDetectedIssueId().GetValue()) - case *dtpb.Reference_DeviceDefinitionId: - return fmt.Sprintf("%s/%s", refType, ref.GetDeviceDefinitionId().GetValue()) - case *dtpb.Reference_DeviceId: - return fmt.Sprintf("%s/%s", refType, ref.GetDeviceId().GetValue()) - case *dtpb.Reference_DeviceMetricId: - return fmt.Sprintf("%s/%s", refType, ref.GetDeviceMetricId().GetValue()) - case *dtpb.Reference_DeviceRequestId: - return fmt.Sprintf("%s/%s", refType, ref.GetDeviceRequestId().GetValue()) - case *dtpb.Reference_DeviceUseStatementId: - return fmt.Sprintf("%s/%s", refType, ref.GetDeviceUseStatementId().GetValue()) - case *dtpb.Reference_DiagnosticReportId: - return fmt.Sprintf("%s/%s", refType, ref.GetDiagnosticReportId().GetValue()) - case *dtpb.Reference_DocumentManifestId: - return fmt.Sprintf("%s/%s", refType, ref.GetDocumentManifestId().GetValue()) - case *dtpb.Reference_DocumentReferenceId: - return fmt.Sprintf("%s/%s", refType, ref.GetDocumentReferenceId().GetValue()) - case *dtpb.Reference_DomainResourceId: - return fmt.Sprintf("%s/%s", refType, ref.GetDomainResourceId().GetValue()) - case *dtpb.Reference_EffectEvidenceSynthesisId: - return fmt.Sprintf("%s/%s", refType, ref.GetEffectEvidenceSynthesisId().GetValue()) - case *dtpb.Reference_EncounterId: - return fmt.Sprintf("%s/%s", refType, ref.GetEncounterId().GetValue()) - case *dtpb.Reference_EndpointId: - return fmt.Sprintf("%s/%s", refType, ref.GetEndpointId().GetValue()) - case *dtpb.Reference_EnrollmentRequestId: - return fmt.Sprintf("%s/%s", refType, ref.GetEnrollmentRequestId().GetValue()) - case *dtpb.Reference_EnrollmentResponseId: - return fmt.Sprintf("%s/%s", refType, ref.GetEnrollmentResponseId().GetValue()) - case *dtpb.Reference_EpisodeOfCareId: - return fmt.Sprintf("%s/%s", refType, ref.GetEpisodeOfCareId().GetValue()) - case *dtpb.Reference_EventDefinitionId: - return fmt.Sprintf("%s/%s", refType, ref.GetEventDefinitionId().GetValue()) - case *dtpb.Reference_EvidenceId: - return fmt.Sprintf("%s/%s", refType, ref.GetEvidenceId().GetValue()) - case *dtpb.Reference_EvidenceVariableId: - return fmt.Sprintf("%s/%s", refType, ref.GetEvidenceVariableId().GetValue()) - case *dtpb.Reference_ExampleScenarioId: - return fmt.Sprintf("%s/%s", refType, ref.GetExampleScenarioId().GetValue()) - case *dtpb.Reference_ExplanationOfBenefitId: - return fmt.Sprintf("%s/%s", refType, ref.GetExplanationOfBenefitId().GetValue()) - case *dtpb.Reference_FamilyMemberHistoryId: - return fmt.Sprintf("%s/%s", refType, ref.GetFamilyMemberHistoryId().GetValue()) - case *dtpb.Reference_FlagId: - return fmt.Sprintf("%s/%s", refType, ref.GetFlagId().GetValue()) - case *dtpb.Reference_Fragment: - return fmt.Sprintf("%s/%s", refType, ref.GetFragment()) - case *dtpb.Reference_GoalId: - return fmt.Sprintf("%s/%s", refType, ref.GetGoalId().GetValue()) - case *dtpb.Reference_GraphDefinitionId: - return fmt.Sprintf("%s/%s", refType, ref.GetGraphDefinitionId().GetValue()) - case *dtpb.Reference_GroupId: - return fmt.Sprintf("%s/%s", refType, ref.GetGroupId().GetValue()) - case *dtpb.Reference_GuidanceResponseId: - return fmt.Sprintf("%s/%s", refType, ref.GetGuidanceResponseId().GetValue()) - case *dtpb.Reference_HealthcareServiceId: - return fmt.Sprintf("%s/%s", refType, ref.GetHealthcareServiceId().GetValue()) - case *dtpb.Reference_ImagingStudyId: - return fmt.Sprintf("%s/%s", refType, ref.GetImagingStudyId().GetValue()) - case *dtpb.Reference_ImmunizationEvaluationId: - return fmt.Sprintf("%s/%s", refType, ref.GetImmunizationEvaluationId().GetValue()) - case *dtpb.Reference_ImmunizationId: - return fmt.Sprintf("%s/%s", refType, ref.GetImmunizationId().GetValue()) - case *dtpb.Reference_ImmunizationRecommendationId: - return fmt.Sprintf("%s/%s", refType, ref.GetImmunizationRecommendationId().GetValue()) - case *dtpb.Reference_ImplementationGuideId: - return fmt.Sprintf("%s/%s", refType, ref.GetImplementationGuideId().GetValue()) - case *dtpb.Reference_InsurancePlanId: - return fmt.Sprintf("%s/%s", refType, ref.GetInsurancePlanId().GetValue()) - case *dtpb.Reference_InvoiceId: - return fmt.Sprintf("%s/%s", refType, ref.GetInvoiceId().GetValue()) - case *dtpb.Reference_LibraryId: - return fmt.Sprintf("%s/%s", refType, ref.GetLibraryId().GetValue()) - case *dtpb.Reference_LinkageId: - return fmt.Sprintf("%s/%s", refType, ref.GetLinkageId().GetValue()) - case *dtpb.Reference_ListId: - return fmt.Sprintf("%s/%s", refType, ref.GetListId().GetValue()) - case *dtpb.Reference_LocationId: - return fmt.Sprintf("%s/%s", refType, ref.GetLocationId().GetValue()) - case *dtpb.Reference_MeasureId: - return fmt.Sprintf("%s/%s", refType, ref.GetMeasureId().GetValue()) - case *dtpb.Reference_MeasureReportId: - return fmt.Sprintf("%s/%s", refType, ref.GetMeasureReportId().GetValue()) - case *dtpb.Reference_MediaId: - return fmt.Sprintf("%s/%s", refType, ref.GetMediaId().GetValue()) - case *dtpb.Reference_MedicationAdministrationId: - return fmt.Sprintf("%s/%s", refType, ref.GetMedicationAdministrationId().GetValue()) - case *dtpb.Reference_MedicationDispenseId: - return fmt.Sprintf("%s/%s", refType, ref.GetMedicationDispenseId().GetValue()) - case *dtpb.Reference_MedicationId: - return fmt.Sprintf("%s/%s", refType, ref.GetMedicationId().GetValue()) - case *dtpb.Reference_MedicationKnowledgeId: - return fmt.Sprintf("%s/%s", refType, ref.GetMedicationKnowledgeId().GetValue()) - case *dtpb.Reference_MedicationRequestId: - return fmt.Sprintf("%s/%s", refType, ref.GetMedicationRequestId().GetValue()) - case *dtpb.Reference_MedicationStatementId: - return fmt.Sprintf("%s/%s", refType, ref.GetMedicationStatementId().GetValue()) - case *dtpb.Reference_MedicinalProductAuthorizationId: - return fmt.Sprintf("%s/%s", refType, ref.GetMedicinalProductAuthorizationId().GetValue()) - case *dtpb.Reference_MedicinalProductContraindicationId: - return fmt.Sprintf("%s/%s", refType, ref.GetMedicinalProductContraindicationId().GetValue()) - case *dtpb.Reference_MedicinalProductId: - return fmt.Sprintf("%s/%s", refType, ref.GetMedicinalProductId().GetValue()) - case *dtpb.Reference_MedicinalProductIndicationId: - return fmt.Sprintf("%s/%s", refType, ref.GetMedicinalProductIndicationId().GetValue()) - case *dtpb.Reference_MedicinalProductIngredientId: - return fmt.Sprintf("%s/%s", refType, ref.GetMedicinalProductIngredientId().GetValue()) - case *dtpb.Reference_MedicinalProductInteractionId: - return fmt.Sprintf("%s/%s", refType, ref.GetMedicinalProductInteractionId().GetValue()) - case *dtpb.Reference_MedicinalProductManufacturedId: - return fmt.Sprintf("%s/%s", refType, ref.GetMedicinalProductManufacturedId().GetValue()) - case *dtpb.Reference_MedicinalProductPackagedId: - return fmt.Sprintf("%s/%s", refType, ref.GetMedicinalProductPackagedId().GetValue()) - case *dtpb.Reference_MedicinalProductPharmaceuticalId: - return fmt.Sprintf("%s/%s", refType, ref.GetMedicinalProductPharmaceuticalId().GetValue()) - case *dtpb.Reference_MedicinalProductUndesirableEffectId: - return fmt.Sprintf("%s/%s", refType, ref.GetMedicinalProductUndesirableEffectId().GetValue()) - case *dtpb.Reference_MessageDefinitionId: - return fmt.Sprintf("%s/%s", refType, ref.GetMessageDefinitionId().GetValue()) - case *dtpb.Reference_MessageHeaderId: - return fmt.Sprintf("%s/%s", refType, ref.GetMessageHeaderId().GetValue()) - case *dtpb.Reference_MetadataResourceId: - return fmt.Sprintf("%s/%s", refType, ref.GetMetadataResourceId().GetValue()) - case *dtpb.Reference_MolecularSequenceId: - return fmt.Sprintf("%s/%s", refType, ref.GetMolecularSequenceId().GetValue()) - case *dtpb.Reference_NamingSystemId: - return fmt.Sprintf("%s/%s", refType, ref.GetNamingSystemId().GetValue()) - case *dtpb.Reference_NutritionOrderId: - return fmt.Sprintf("%s/%s", refType, ref.GetNutritionOrderId().GetValue()) - case *dtpb.Reference_ObservationDefinitionId: - return fmt.Sprintf("%s/%s", refType, ref.GetObservationDefinitionId().GetValue()) - case *dtpb.Reference_ObservationId: - return fmt.Sprintf("%s/%s", refType, ref.GetObservationId().GetValue()) - case *dtpb.Reference_OperationDefinitionId: - return fmt.Sprintf("%s/%s", refType, ref.GetOperationDefinitionId().GetValue()) - case *dtpb.Reference_OperationOutcomeId: - return fmt.Sprintf("%s/%s", refType, ref.GetOperationOutcomeId().GetValue()) - case *dtpb.Reference_OrganizationAffiliationId: - return fmt.Sprintf("%s/%s", refType, ref.GetOrganizationAffiliationId().GetValue()) - case *dtpb.Reference_OrganizationId: - return fmt.Sprintf("%s/%s", refType, ref.GetOrganizationId().GetValue()) - case *dtpb.Reference_ParametersId: - return fmt.Sprintf("%s/%s", refType, ref.GetParametersId().GetValue()) - case *dtpb.Reference_PatientId: - return fmt.Sprintf("%s/%s", refType, ref.GetPatientId().GetValue()) - case *dtpb.Reference_PaymentNoticeId: - return fmt.Sprintf("%s/%s", refType, ref.GetPaymentNoticeId().GetValue()) - case *dtpb.Reference_PaymentReconciliationId: - return fmt.Sprintf("%s/%s", refType, ref.GetPaymentReconciliationId().GetValue()) - case *dtpb.Reference_PersonId: - return fmt.Sprintf("%s/%s", refType, ref.GetPersonId().GetValue()) - case *dtpb.Reference_PlanDefinitionId: - return fmt.Sprintf("%s/%s", refType, ref.GetPlanDefinitionId().GetValue()) - case *dtpb.Reference_PractitionerId: - return fmt.Sprintf("%s/%s", refType, ref.GetPractitionerId().GetValue()) - case *dtpb.Reference_PractitionerRoleId: - return fmt.Sprintf("%s/%s", refType, ref.GetPractitionerRoleId().GetValue()) - case *dtpb.Reference_ProcedureId: - return fmt.Sprintf("%s/%s", refType, ref.GetProcedureId().GetValue()) - case *dtpb.Reference_ProvenanceId: - return fmt.Sprintf("%s/%s", refType, ref.GetProvenanceId().GetValue()) - case *dtpb.Reference_QuestionnaireId: - return fmt.Sprintf("%s/%s", refType, ref.GetQuestionnaireId().GetValue()) - case *dtpb.Reference_QuestionnaireResponseId: - return fmt.Sprintf("%s/%s", refType, ref.GetQuestionnaireResponseId().GetValue()) - case *dtpb.Reference_RelatedPersonId: - return fmt.Sprintf("%s/%s", refType, ref.GetRelatedPersonId().GetValue()) - case *dtpb.Reference_RequestGroupId: - return fmt.Sprintf("%s/%s", refType, ref.GetRequestGroupId().GetValue()) - case *dtpb.Reference_ResearchDefinitionId: - return fmt.Sprintf("%s/%s", refType, ref.GetResearchDefinitionId().GetValue()) - case *dtpb.Reference_ResearchElementDefinitionId: - return fmt.Sprintf("%s/%s", refType, ref.GetResearchElementDefinitionId().GetValue()) - case *dtpb.Reference_ResearchStudyId: - return fmt.Sprintf("%s/%s", refType, ref.GetResearchStudyId().GetValue()) - case *dtpb.Reference_ResearchSubjectId: - return fmt.Sprintf("%s/%s", refType, ref.GetResearchSubjectId().GetValue()) - case *dtpb.Reference_ResourceId: - return fmt.Sprintf("%s/%s", refType, ref.GetResourceId()) - case *dtpb.Reference_RiskAssessmentId: - return fmt.Sprintf("%s/%s", refType, ref.GetRiskAssessmentId().GetValue()) - case *dtpb.Reference_RiskEvidenceSynthesisId: - return fmt.Sprintf("%s/%s", refType, ref.GetRiskEvidenceSynthesisId().GetValue()) - case *dtpb.Reference_ScheduleId: - return fmt.Sprintf("%s/%s", refType, ref.GetScheduleId().GetValue()) - case *dtpb.Reference_SearchParameterId: - return fmt.Sprintf("%s/%s", refType, ref.GetSearchParameterId().GetValue()) - case *dtpb.Reference_ServiceRequestId: - return fmt.Sprintf("%s/%s", refType, ref.GetServiceRequestId().GetValue()) - case *dtpb.Reference_SlotId: - return fmt.Sprintf("%s/%s", refType, ref.GetSlotId().GetValue()) - case *dtpb.Reference_SpecimenDefinitionId: - return fmt.Sprintf("%s/%s", refType, ref.GetSpecimenDefinitionId().GetValue()) - case *dtpb.Reference_SpecimenId: - return fmt.Sprintf("%s/%s", refType, ref.GetSpecimenId().GetValue()) - case *dtpb.Reference_StructureDefinitionId: - return fmt.Sprintf("%s/%s", refType, ref.GetStructureDefinitionId().GetValue()) - case *dtpb.Reference_StructureMapId: - return fmt.Sprintf("%s/%s", refType, ref.GetStructureMapId().GetValue()) - case *dtpb.Reference_SubscriptionId: - return fmt.Sprintf("%s/%s", refType, ref.GetSubscriptionId().GetValue()) - case *dtpb.Reference_SubstanceId: - return fmt.Sprintf("%s/%s", refType, ref.GetSubstanceId().GetValue()) - case *dtpb.Reference_SubstanceNucleicAcidId: - return fmt.Sprintf("%s/%s", refType, ref.GetSubstanceNucleicAcidId().GetValue()) - case *dtpb.Reference_SubstancePolymerId: - return fmt.Sprintf("%s/%s", refType, ref.GetSubstancePolymerId().GetValue()) - case *dtpb.Reference_SubstanceProteinId: - return fmt.Sprintf("%s/%s", refType, ref.GetSubstanceProteinId().GetValue()) - case *dtpb.Reference_SubstanceReferenceInformationId: - return fmt.Sprintf("%s/%s", refType, ref.GetSubstanceReferenceInformationId().GetValue()) - case *dtpb.Reference_SubstanceSourceMaterialId: - return fmt.Sprintf("%s/%s", refType, ref.GetSubstanceSourceMaterialId().GetValue()) - case *dtpb.Reference_SubstanceSpecificationId: - return fmt.Sprintf("%s/%s", refType, ref.GetSubstanceSpecificationId().GetValue()) - case *dtpb.Reference_SupplyDeliveryId: - return fmt.Sprintf("%s/%s", refType, ref.GetSupplyDeliveryId().GetValue()) - case *dtpb.Reference_SupplyRequestId: - return fmt.Sprintf("%s/%s", refType, ref.GetSupplyRequestId().GetValue()) - case *dtpb.Reference_TaskId: - return fmt.Sprintf("%s/%s", refType, ref.GetTaskId().GetValue()) - case *dtpb.Reference_TerminologyCapabilitiesId: - return fmt.Sprintf("%s/%s", refType, ref.GetTerminologyCapabilitiesId().GetValue()) - case *dtpb.Reference_TestReportId: - return fmt.Sprintf("%s/%s", refType, ref.GetTestReportId().GetValue()) - case *dtpb.Reference_TestScriptId: - return fmt.Sprintf("%s/%s", refType, ref.GetTestScriptId().GetValue()) - case *dtpb.Reference_Uri: - return fmt.Sprintf("%s/%s", refType, ref.GetUri()) - case *dtpb.Reference_ValueSetId: - return fmt.Sprintf("%s/%s", refType, ref.GetValueSetId().GetValue()) - case *dtpb.Reference_VerificationResultId: - return fmt.Sprintf("%s/%s", refType, ref.GetVerificationResultId().GetValue()) - case *dtpb.Reference_VisionPrescriptionId: - return fmt.Sprintf("%s/%s", refType, ref.GetVisionPrescriptionId().GetValue()) - default: + r := proto.Clone(ref).(*dtpb.Reference) + if err := jsonformat.DenormalizeReference(r); err != nil { return "" } + return r.GetUri().GetValue() } func toString(item any) string { @@ -368,7 +67,10 @@ func Resolve(ctx *expr.Context, input system.Collection, args ...expr.Expression toResolve := []string{} for _, item := range input { if isValidResolveInput(item) { - toResolve = append(toResolve, toString(item)) + ref := toString(item) + if ref != "" { + toResolve = append(toResolve, ref) + } } } diff --git a/fhirpath/internal/funcs/impl/resolve_test.go b/fhirpath/internal/funcs/impl/resolve_test.go index f13576f..a5bbba9 100644 --- a/fhirpath/internal/funcs/impl/resolve_test.go +++ b/fhirpath/internal/funcs/impl/resolve_test.go @@ -8,6 +8,7 @@ import ( ppb "github.com/google/fhir/go/proto/google/fhir/proto/r4/core/resources/patient_go_proto" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" + "github.com/verily-src/fhirpath-go/fhirpath" "github.com/verily-src/fhirpath-go/fhirpath/internal/expr" "github.com/verily-src/fhirpath-go/fhirpath/internal/funcs/impl" "github.com/verily-src/fhirpath-go/fhirpath/resolver" @@ -23,11 +24,20 @@ func TestResolve(t *testing.T) { } var patientChuRef = &dtpb.Reference{ - Type: fhir.URI("Patient"), Reference: &dtpb.Reference_PatientId{ PatientId: &dtpb.ReferenceId{Value: "123"}}, } + var patientChuRefUri = &dtpb.Reference{ + Reference: &dtpb.Reference_Uri{ + Uri: &dtpb.String{Value: "Patient/123"}}, + } + + var patientChuRefFragment = &dtpb.Reference{ + Reference: &dtpb.Reference_Fragment{ + Fragment: &dtpb.String{Value: "123"}}, + } + var patientChuUri = &dtpb.Uri{ Value: "Patient/123", } @@ -57,31 +67,43 @@ func TestResolve(t *testing.T) { { name: "happy path; successful reference resolution", inputCollection: system.Collection{patientChuRef}, - resolverImpl: resolvertest.HappyResolver(patientChu), + resolverImpl: resolvertest.SimpleResolver(map[string][]fhirpath.Resource{"Patient/123": {patientChu}}), + wantCollection: system.Collection{patientChu}, + }, + { + name: "happy path; successful reference with uri resolution", + inputCollection: system.Collection{patientChuRefUri}, + resolverImpl: resolvertest.SimpleResolver(map[string][]fhirpath.Resource{"Patient/123": {patientChu}}), + wantCollection: system.Collection{patientChu}, + }, + { + name: "happy path; successful reference with fragment resolution", + inputCollection: system.Collection{patientChuRefFragment}, + resolverImpl: resolvertest.SimpleResolver(map[string][]fhirpath.Resource{"#123": {patientChu}}), wantCollection: system.Collection{patientChu}, }, { name: "happy path; successful uri resolution", inputCollection: system.Collection{patientChuUri}, - resolverImpl: resolvertest.HappyResolver(patientChu), + resolverImpl: resolvertest.SimpleResolver(map[string][]fhirpath.Resource{"Patient/123": {patientChu}}), wantCollection: system.Collection{patientChu}, }, { name: "happy path; successful string resolution", inputCollection: system.Collection{patientChuString}, - resolverImpl: resolvertest.HappyResolver(patientChu), + resolverImpl: resolvertest.SimpleResolver(map[string][]fhirpath.Resource{"Patient/123": {patientChu}}), wantCollection: system.Collection{patientChu}, }, { name: "happy path; successful url resolution", inputCollection: system.Collection{patientChuUrl}, - resolverImpl: resolvertest.HappyResolver(patientChu), + resolverImpl: resolvertest.SimpleResolver(map[string][]fhirpath.Resource{"http://example.com/Patient/123": {patientChu}}), wantCollection: system.Collection{patientChu}, }, { name: "happy path; successful canonical resolution", inputCollection: system.Collection{patientChuCanonical}, - resolverImpl: resolvertest.HappyResolver(patientChu), + resolverImpl: resolvertest.SimpleResolver(map[string][]fhirpath.Resource{"Patient/123": {patientChu}}), wantCollection: system.Collection{patientChu}, }, { @@ -111,6 +133,18 @@ func TestResolve(t *testing.T) { resolverImpl: resolvertest.ErroringResolver(resolveErr), wantErr: resolveErr, }, + { + name: "no matching resources - returns empty collection", + inputCollection: system.Collection{patientChuRef}, + resolverImpl: resolvertest.SimpleResolver(map[string][]fhirpath.Resource{"Patient/999": {patientChu}}), + wantCollection: system.Collection{}, + }, + { + name: "no matching resources - returns empty collection", + inputCollection: system.Collection{patientChuRef}, + resolverImpl: resolvertest.SimpleResolver(map[string][]fhirpath.Resource{"Patient/999": {patientChu}}), + wantCollection: system.Collection{}, + }, } for _, tc := range testCases { diff --git a/fhirpath/resolver/resolvertest/resolvertest.go b/fhirpath/resolver/resolvertest/resolvertest.go index fc63e16..5940195 100644 --- a/fhirpath/resolver/resolvertest/resolvertest.go +++ b/fhirpath/resolver/resolvertest/resolvertest.go @@ -28,3 +28,17 @@ func ErroringResolver(err error) resolver.Resolver { return nil, err }) } + +// SimpleResolver returns a resolver.Resolver that uses the reference as a key to lookup for the +// resource in a map. +func SimpleResolver(resources map[string][]fhirpath.Resource) resolver.Resolver { + return resolverFunc(func(input []string) ([]fhir.Resource, error) { + var result []fhir.Resource + for _, ref := range input { + if v, ok := resources[ref]; ok { + result = append(result, v...) + } + } + return result, nil + }) +}