public void TestResolve() { var statement = "Bundle.entry.where(fullUrl = 'http://example.org/fhir/Patient/e')" + ".resource.managingOrganization.resolve().id"; var result = _bundleElement.Select(statement); Assert.AreEqual(1, result.Count()); Assert.AreEqual("orgY", result.First().Value); //var resultPoco = _parsed.Select(statement).SingleOrDefault() as Organization; //Assert.IsNotNull(resultPoco); //Assert.AreEqual("orgY", resultPoco.Id); }
public SearchParameterWrapper(ITypedElement searchParameter) { EnsureArg.IsNotNull(searchParameter, nameof(searchParameter)); EnsureArg.Is(KnownResourceTypes.SearchParameter, searchParameter.InstanceType, StringComparison.Ordinal, nameof(searchParameter)); _name = new Lazy <string>(() => searchParameter.Scalar("name")?.ToString()); _description = new Lazy <string>(() => searchParameter.Scalar("description")?.ToString()); _url = new Lazy <string>(() => searchParameter.Scalar("url")?.ToString()); _expression = new Lazy <string>(() => searchParameter.Scalar("expression")?.ToString()); _type = new Lazy <string>(() => searchParameter.Scalar("type")?.ToString()); _base = new Lazy <IReadOnlyList <string> >(() => searchParameter.Select("base")?.AsStringValues().ToArray()); _component = new Lazy <IReadOnlyList <ITypedElement> >(() => searchParameter.Select("component")?.ToArray()); _target = new Lazy <IReadOnlyList <string> >(() => searchParameter.Select("target")?.AsStringValues().ToArray()); }
public static void CheckBundleEntryNavigation(ITypedElement node) { var entryNav = node.Select("entry.resource").First(); var id = entryNav.Scalar("id"); Assert.IsNotNull(id); }
protected override IEnumerable <ISearchValue> Convert(ITypedElement value) { // Based on spec: http://hl7.org/fhir/search.html#token, // CodeableConcept.text is searchable, but we will only create a dedicated entry for it // if it is different from the display text of one of its codings string text = value.Scalar("text") as string; bool conceptTextNeedsToBeAdded = !string.IsNullOrWhiteSpace(text); foreach (ITypedElement coding in value.Select("coding")) { TokenSearchValue searchValue = coding?.ToTokenSearchValue(); if (searchValue != null) { if (conceptTextNeedsToBeAdded) { conceptTextNeedsToBeAdded = !string.Equals(text, searchValue.Text, StringComparison.OrdinalIgnoreCase); } yield return(searchValue); } } if (conceptTextNeedsToBeAdded) { yield return(new TokenSearchValue(null, null, text)); } }
private IEnumerable <ITypedElement> GetMatchNodes(AnonymizationFhirPathRule rule, ITypedElement node) { var typeMatch = AnonymizationFhirPathRule.TypeRuleRegex.Match(rule.Path); var nameMatch = AnonymizationFhirPathRule.NameRuleRegex.Match(rule.Path); if (typeMatch.Success) { return(GetMatchNodesFromLookUp(_typeToNodeLookUp, typeMatch.Groups["type"].Value, typeMatch.Groups["expression"].Value)); } if (nameMatch.Success) { return(GetMatchNodesFromLookUp(_nameToNodeLookUp, nameMatch.Groups["name"].Value, nameMatch.Groups["expression"].Value)); } /* * Special case handling: * Senario: FHIR path only contains resourceType: Patient, Resource. * Sample AnonymizationFhirPathRule: { "path": "Patient", "method": "keep" } * * Current FHIR path lib do not support navigate such ResourceType FHIR path from resource in bundle. * Example: navigate with FHIR path "Patient" from "Bundle.entry[0].resource[0]" is not support */ return(rule.IsResourceTypeRule ? new List <ITypedElement> { node } : node.Select(rule.Expression).ToList()); }
public BundleWrapper(ITypedElement bundle) { EnsureArg.IsNotNull(bundle, nameof(bundle)); EnsureArg.Is(KnownResourceTypes.Bundle, bundle.InstanceType, StringComparison.Ordinal, nameof(bundle)); _entries = new Lazy <IReadOnlyList <BundleEntryWrapper> >(() => bundle.Select("entry").Select(x => new BundleEntryWrapper(x)).ToArray()); }
private IEnumerable <SearchIndexEntry> ProcessCompositeSearchParameter(SearchParameterInfo searchParameter, ITypedElement resource, EvaluationContext context) { Debug.Assert(searchParameter?.Type == SearchParamType.Composite, "The search parameter must be composite."); SearchParameterInfo compositeSearchParameterInfo = searchParameter; IEnumerable <ITypedElement> rootObjects = resource.Select(searchParameter.Expression, context); foreach (var rootObject in rootObjects) { int numberOfComponents = searchParameter.Component.Count; bool skip = false; var componentValues = new IReadOnlyList <ISearchValue> [numberOfComponents]; // For each object extracted from the expression, we will need to evaluate each component. for (int i = 0; i < numberOfComponents; i++) { SearchParameterComponentInfo component = searchParameter.Component[i]; // First find the type of the component. SearchParameterInfo componentSearchParameterDefinition = searchParameter.Component[i].ResolvedSearchParameter; IReadOnlyList <ISearchValue> extractedComponentValues = ExtractSearchValues( componentSearchParameterDefinition.Url.ToString(), componentSearchParameterDefinition.Type, componentSearchParameterDefinition.TargetResourceTypes, rootObject, component.Expression, context); // Filter out any search value that's not valid as a composite component. extractedComponentValues = extractedComponentValues .Where(sv => sv.IsValidAsCompositeComponent) .ToArray(); if (!extractedComponentValues.Any()) { // One of the components didn't have any value and therefore it will not be indexed. skip = true; break; } componentValues[i] = extractedComponentValues; } if (skip) { continue; } yield return(new SearchIndexEntry(compositeSearchParameterInfo, new CompositeSearchValue(componentValues))); } }
public void TestStringSplit() { ITypedElement dummy = ElementNode.ForPrimitive("a,b,c,d"); var result = dummy.Select("split(',')"); Assert.IsNotNull(result); CollectionAssert.AreEqual(new[] { "a", "b", "c", "d" }, result.Select(r => r.Value.ToString()).ToArray()); dummy = ElementNode.ForPrimitive("a,,b,c,d"); // Empty element should be removed result = dummy.Select("split(',')"); Assert.IsNotNull(result); CollectionAssert.AreEqual(new[] { "a", "b", "c", "d" }, result.Select(r => r.Value.ToString()).ToArray()); dummy = ElementNode.ForPrimitive(""); result = dummy.Select("split(',')"); Assert.IsNotNull(result); dummy = ElementNode.ForPrimitive("[stop]ONE[stop][stop]TWO[stop][stop][stop]THREE[stop][stop]"); result = dummy.Select("split('[stop]')"); Assert.IsNotNull(result); CollectionAssert.AreEqual(new[] { "ONE", "TWO", "THREE" }, result.Select(r => r.Value.ToString()).ToArray()); }
protected override IEnumerable <ISearchValue> Convert(ITypedElement value) { // http://hl7.org/fhir/patient.html recommends the following: // A server defined search that may match any of the string fields in the Address, including line, city, state, country, postalCode, and/or text. // we will do a basic search based on any of the address component for now. Details on localization will be handled later. var city = value.Scalar("city") as string; var country = value.Scalar("country") as string; var district = value.Scalar("district") as string; IEnumerable <ITypedElement> lines = value.Select("line"); var postCode = value.Scalar("postalCode") as string; var state = value.Scalar("state") as string; var text = value.Scalar("text") as string; if (!string.IsNullOrWhiteSpace(city)) { yield return(new StringSearchValue(city)); } if (!string.IsNullOrWhiteSpace(country)) { yield return(new StringSearchValue(country)); } if (!string.IsNullOrWhiteSpace(district)) { yield return(new StringSearchValue(district)); } foreach (var line in lines.AsStringValues()) { yield return(new StringSearchValue(line)); } if (!string.IsNullOrWhiteSpace(postCode)) { yield return(new StringSearchValue(postCode)); } if (!string.IsNullOrWhiteSpace(state)) { yield return(new StringSearchValue(state)); } if (!string.IsNullOrWhiteSpace(text)) { yield return(new StringSearchValue(text)); } }
protected override IEnumerable <ISearchValue> Convert(ITypedElement value) { IEnumerable <ITypedElement> givenNames = value.Select("given"); IEnumerable <ITypedElement> prefixes = value.Select("prefix"); IEnumerable <ITypedElement> suffixes = value.Select("suffix"); var family = value.Scalar("family") as string; var text = value.Scalar("text") as string; // https://www.hl7.org/fhir/patient.html recommends the following: // A server defined search that may match any of the string fields in the HumanName, including family, give, prefix, suffix, suffix, and/or text // we will do a basic search based on family or given or prefix or suffix or text for now. Details on localization will be handled later. foreach (var given in givenNames.AsStringValues()) { yield return(new StringSearchValue(given)); } if (!string.IsNullOrWhiteSpace(family)) { yield return(new StringSearchValue(family)); } foreach (var prefix in prefixes.AsStringValues()) { yield return(new StringSearchValue(prefix)); } foreach (var suffix in suffixes.AsStringValues()) { yield return(new StringSearchValue(suffix)); } if (!string.IsNullOrWhiteSpace(text)) { yield return(new StringSearchValue(text)); } }
public IEnumerable <ITypedElement>?Select(IFhirResourceStu3 fhirResource, string Expression) { ITypedElement TypedElement = fhirResource.Stu3.ToTypedElement(); //FhirPathCompiler.DefaultSymbolTable.AddFhirExtensions(); var oFhirEvaluationContext = new Hl7.Fhir.FhirPath.FhirEvaluationContext(TypedElement); //The resolve() function then also needs to be provided an external resolver delegate that performs the resolve //that delegate can be set as below. Here I am providing my own implementation 'IPyroFhirPathResolve.Resolver' //oFhirEvaluationContext.ElementResolver = IPyroFhirPathResolve.Resolver; try { return(TypedElement.Select(Expression, oFhirEvaluationContext)); } catch (Exception Exec) { throw new Bug.Common.Exceptions.FhirFatalException(System.Net.HttpStatusCode.InternalServerError, $"Unable to evaluate the SearchParameter FhirPath expression of: {Expression} for FHIR {FhirVersion.Stu3.GetCode()}. See inner exception for more info.", Exec); } }
static void Main(string[] args) { var xml = "<Patient xmlns=\"http://hl7.org/fhir\"><identifier>" + "<use value=\"official\" /></identifier></Patient>"; MemoryStream memStream = new MemoryStream(); byte[] data = Encoding.Default.GetBytes(xml); memStream.Write(data, 0, data.Length); memStream.Position = 0; XmlReader reader = XmlReader.Create(memStream); reader.Read(); FhirXmlParsingSettings settings = new FhirXmlParsingSettings(); ISourceNode patientNode = FhirXmlNode.Read(reader, settings); //IResourceResolver Resolver = new TestResourceResolver(); IResourceResolver Resolver = ZipSource.CreateValidationSource(); StructureDefinitionSummaryProvider Provider = new StructureDefinitionSummaryProvider(Resolver); ITypedElement patientRootElement = patientNode.ToTypedElement(Provider); var r = patientRootElement.Select("Patient.identifier.use"); string test = (string)r.FirstOrDefault().Value; //ITypedElement activeElement = patientRootElement.Children("active").First(); //Assert.AreEqual("boolean", activeElement.Type); //Assert.AreEqual("boolean", activeElement.Type); //var patientNode = FhirXmlNode.Parse(xml); //var use = patientNode.Children("identifier").Children("use").First(); //Assert.AreEqual("official", use.Text); //Assert.AreEqual("Patient.identifier[0].use[0]", use.Location); }
private IReadOnlyList <ISearchValue> ExtractSearchValues( string searchParameterDefinitionUrl, SearchParamType?searchParameterType, IReadOnlyList <string> allowedReferenceResourceTypes, ITypedElement element, string fhirPathExpression, EvaluationContext context) { Debug.Assert(searchParameterType != SearchParamType.Composite, "The search parameter must be non-composite."); var results = new List <ISearchValue>(); // For simple value type, we can parse the expression directly. IEnumerable <ITypedElement> extractedValues = Enumerable.Empty <ITypedElement>(); try { extractedValues = element.Select(fhirPathExpression, context); } catch (Exception ex) { _logger.LogWarning( ex, "Failed to extract the values using '{FhirPathExpression}' against '{ElementType}'.", fhirPathExpression, element.GetType()); } Debug.Assert(extractedValues != null, "The extracted values should not be null."); // If there is target set, then filter the extracted values to only those types. if (searchParameterType == SearchParamType.Reference && allowedReferenceResourceTypes?.Count > 0) { List <string> targetResourceTypes = _targetTypesLookup.GetOrAdd(searchParameterDefinitionUrl, _ => { return(allowedReferenceResourceTypes.Select(t => t.ToString()).ToList()); }); // TODO: The expression for reference search parameters in STU3 has issues. // The reference search parameter could be pointing to an element that can be multiple types. For example, // the Appointment.participant.actor can be type of Patient, Practitioner, Related Person, Location, and so on. // Some search parameter could refer to this property but restrict to certain types. For example, // Appointment's location search parameter is returned only when Appointment.participant.actor is Location element. // The STU3 expressions don't have this restriction so everything is being returned. This is addressed in R4 release (see // http://community.fhir.org/t/expression-seems-incorrect-for-reference-search-parameter-thats-only-applicable-to-certain-types/916/2). // Therefore, for now, we will need to compare the reference value itself (which can be internal or external references), and restrict // the values ourselves. extractedValues = extractedValues.Where(ev => { if (ev.InstanceType.Equals("ResourceReference", StringComparison.OrdinalIgnoreCase)) { return(ev.Scalar("reference") is string rr && targetResourceTypes.Any(trt => rr.Contains(trt, StringComparison.Ordinal))); } return(true); }); } foreach (var extractedValue in extractedValues) { if (!_fhirElementTypeConverterManager.TryGetConverter(extractedValue.InstanceType, GetSearchValueTypeForSearchParamType(searchParameterType), out ITypedElementToSearchValueConverter converter)) { _logger.LogWarning( "The FHIR element '{ElementType}' is not supported.", extractedValue.InstanceType); continue; } IEnumerable <ISearchValue> searchValues = converter.ConvertTo(extractedValue); if (searchValues != null) { if (searchParameterType == SearchParamType.Reference && allowedReferenceResourceTypes?.Count == 1) { // For references, if the type is not specified in the reference string, we can set the type on the search value because // in this case it can only be of one type. string singleAllowedResourceType = allowedReferenceResourceTypes[0]; foreach (ISearchValue searchValue in searchValues) { if (searchValue is ReferenceSearchValue rsr && string.IsNullOrEmpty(rsr.ResourceType)) { results.Add(new ReferenceSearchValue(rsr.Kind, rsr.BaseUri, singleAllowedResourceType, rsr.ResourceId)); } else { results.Add(searchValue); } } }
internal BundleEntryWrapper(ITypedElement entry) { EnsureArg.IsNotNull(entry, nameof(entry)); _entry = new Lazy <ITypedElement>(() => entry.Select("resource").FirstOrDefault()); }