Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/Hl7.Fhir.Specification.Tests/DiscriminatorInterpreterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,22 @@ public async T.Task WalkToInlineExtensionConstraints()
Assert.AreEqual("hi!", ((FhirString)elem.Current.Current.Fixed).Value);
}

[TestMethod]
public async T.Task WalkToInlineComplexExtensionConstraints()
{
var sd = await _source.FindStructureDefinitionAsync("http://unittest.com/StructureDefinition/patient-sliced-complex-extension");
var nav = ElementDefinitionNavigator.ForSnapshot(sd);
nav.JumpToFirst("Patient.communication");
nav.MoveToNextSlice();
var walker = new StructureDefinitionWalker(nav, _source);

var elem = walker.Walk("extension('http://hl7.org/fhir/StructureDefinition/patient-proficiency').extension('type').value").Single();
var pattern = elem.Current.Current.Pattern;

Assert.IsTrue(pattern is Coding c && c.Code == "RSP");
}


[TestMethod]
public async T.Task ParseInvalidDiscriminatorExpressions()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,7 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>

<ItemGroup>
<None Remove="TestData\validation\list-with-profile-slicing.xml" />
<None Remove="TestData\validation\observation-profile-for-discriminator-test.xml" />
</ItemGroup>


<ItemGroup>
<Content Include="..\Hl7.Fhir.Specification\data\profiles-resources.xml" Link="TestData\snapshot-test\profiles-resources.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
Expand Down
23 changes: 21 additions & 2 deletions src/Hl7.Fhir.Specification.Tests/StructureDefinitionWalkerTests.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Hl7.Fhir.Specification.Source;
using Hl7.Fhir.Model;
using Hl7.Fhir.Serialization;
using Hl7.Fhir.Specification.Navigation;
using Hl7.Fhir.Specification.Source;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Linq;
using T = System.Threading.Tasks;

Expand Down Expand Up @@ -112,6 +113,24 @@ public async T.Task WalkAcrossReference()
var elem = walker.Child("performer").Resolve().OfType("Practitioner").Child("name").Single();
Assert.AreEqual("Practitioner.name", elem.Current.Path);
}

[TestMethod]
public async T.Task WalkAcrossInlineExtension()
{
var sd = await _source.FindStructureDefinitionAsync("http://unittest.com/StructureDefinition/patient-sliced-complex-extension");
var nav = ElementDefinitionNavigator.ForSnapshot(sd);
nav.JumpToFirst("Patient.communication");
nav.MoveToNextSlice();

var fortest = nav.StructureDefinition.ToXml();
var walker = new StructureDefinitionWalker(nav, _source);

var elem = walker.Extension("http://hl7.org/fhir/StructureDefinition/patient-proficiency");
elem = elem.Extension("type");

Assert.IsNotNull(elem);
Assert.AreEqual("Patient.communication:languageControlListening.extension:languageControlListening-proficiency.extension:type", elem.Current.Current.ElementId);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,6 @@
<rules value="open"/>
</slicing>
</element>
<element id="Observation.identifier.extension">
<path value="Observation.identifier.extension"/>
<slicing>
<discriminator>
<type value="value"/>
<path value="url"/>
</discriminator>
<rules value="open"/>
</slicing>
</element>
<element id="Observation.identifier.extension:myExtension">
<path value="Observation.identifier.extension"/>
<sliceName value="myExtension"/>
<type>
<code value="Extension"/>
<profile value="http://example.org/fhir/StructureDefinition/string-extension-for-discriminator-test"/>
</type>
</element>
<element id="Observation.identifier:someCustomIdentifier">
<path value="Observation.identifier"/>
<sliceName value="someCustomIdentifier"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
<?xml version="1.0" encoding="utf-8"?>
<StructureDefinition xmlns="http://hl7.org/fhir">
<id value="zib-Patient"/>
<url value="http://unittest.com/StructureDefinition/patient-sliced-complex-extension"/>
<version value="0.0.1"/>
<name value="ZibPatient"/>
<title value="zib Patient"/>
<status value="draft"/>
<experimental value="false"/>
<publisher value="Nictiz"/>
<contact>
<name value="Nictiz"/>
<telecom>
<system value="email"/>
<value value="info@nictiz.nl"/>
<use value="work"/>
</telecom>
</contact>
<description value="A person who receives medical, psychological, paramedical or nursing care. In some care settings, the term client or participant is used instead of the term patient.&#xD;&#xA;Nationality is used as an indication of the country of citizenship. &#xD;&#xA;Marital status is a person’s formal position as defined by the Dutch civil code. There is a distinction between marriage and civil union."/>
<purpose value="This Patient resource represents the Dutch zib ('Zorginformatiebouwsteen', i.e. Health and Care Information Model) [Patient v3.2 (2020)](https://zibs.nl/wiki/Patient-v3.2(2020EN)), [Nationality v3.0 (2020)](https://zibs.nl/wiki/Nationality-v3.0(2020EN)) and [MaritalStatus v3.1 (2020)](https://zibs.nl/wiki/MaritalStatus-v3.1(2020EN))."/>
<copyright value="Copyright and related rights waived via CC0, https://creativecommons.org/publicdomain/zero/1.0/. This does not apply to information from third parties, for example a medical terminology system. The implementer alone is responsible for identifying and obtaining any necessary licenses or authorizations to utilize third party IP in connection with the specification or otherwise."/>
<fhirVersion value="4.0.1"/>
<mapping>
<identity value="zib-patient-v3.2-2020EN"/>
<uri value="https://zibs.nl/wiki/Patient-v3.2(2020EN)"/>
<name value="zib Patient-v3.2(2020EN)"/>
</mapping>
<mapping>
<identity value="zib-nationality-v3.0-2020EN"/>
<uri value="https://zibs.nl/wiki/Nationality-v3.0(2020EN)"/>
<name value="zib Nationality-v3.0(2020EN)"/>
</mapping>
<mapping>
<identity value="zib-maritalstatus-v3.1-2020EN"/>
<uri value="https://zibs.nl/wiki/MaritalStatus-v3.1(2020EN)"/>
<name value="zib MaritalStatus-v3.1(2020EN)"/>
</mapping>
<mapping>
<identity value="zib-languageproficiency-v3.2-2020EN"/>
<uri value="https://zibs.nl/wiki/LanguageProficiency-v3.2(2020EN)"/>
<name value="zib LanguageProficiency-v3.2(2020EN)"/>
</mapping>
<mapping>
<identity value="zib-contactinformation-v1.2-2020EN"/>
<uri value="https://zibs.nl/wiki/ContactInformation-v1.2(2020EN)"/>
<name value="zib ContactInformation-v1.2(2020EN)"/>
</mapping>
<mapping>
<identity value="zib-contactperson-v3.4-2020EN"/>
<uri value="https://zibs.nl/wiki/ContactPerson-v3.4(2020EN)"/>
<name value="zib ContactPerson-v3.4(2020EN)"/>
</mapping>
<kind value="resource"/>
<abstract value="true"/>
<type value="Patient"/>
<baseDefinition value="http://hl7.org/fhir/StructureDefinition/Patient"/>
<derivation value="constraint"/>
<differential>
<element id="Patient">
<path value="Patient"/>
<short value="Patient"/>
<alias value="Patient"/>
<mapping>
<identity value="zib-patient-v3.2-2020EN"/>
<map value="NL-CM:0.1.1"/>
<comment value="Patient"/>
</mapping>
</element>
<element id="Patient.communication">
<path value="Patient.communication"/>
<short value="LanguageProficiency"/>
<definition value="Root concept of the LanguageProficiency information model. This concept contains all data elements of the LanguageProficiency information model."/>
<alias value="Taalvaardigheid"/>
<mapping>
<identity value="zib-languageproficiency-v3.2-2020EN"/>
<map value="NL-CM:7.12.1"/>
<comment value="LanguageProficiency"/>
</mapping>
</element>
<element id="Patient.communication">
<path value="Patient.communication"/>
<slicing>
<discriminator>
<type value="value"/>
<path value="extension('http://hl7.org/fhir/StructureDefinition/patient-proficiency').extension('type').value"/>
</discriminator>
<rules value="open"/>
</slicing>
</element>
<element id="Patient.communication:languageControlListening">
<path value="Patient.communication"/>
<sliceName value="languageControlListening"/>
</element>
<element id="Patient.communication:languageControlListening.extension">
<path value="Patient.communication.extension"/>
<sliceName value="languageControlListening-proficiency" />
<short value="LanguageControlListening"/>
<definition value="The ability to understand spoken text in the language in question."/>
<alias value="TaalvaardigheidBegrijpen"/>
<max value="1"/>
<type>
<code value="Extension"/>
<profile value="http://hl7.org/fhir/StructureDefinition/patient-proficiency"/>
</type>
<mapping>
<identity value="zib-languageproficiency-v3.2-2020EN"/>
<map value="NL-CM:7.12.6"/>
<comment value="LanguageControlListening"/>
</mapping>
</element>
<element id="Patient.communication:languageControlListening.extension:languageControlListening-proficiency.extension:level">
<path value="Patient.communication.extension.extension"/>
<sliceName value="level"/>
<min value="1"/>
</element>
<element id="Patient.communication:languageControlListening.extension:languageControlListening-proficiency.extension:level.value[x]">
<path value="Patient.communication.extension.extension.value[x]"/>
<comment value="The zib ValueSet TaalvaardigheidBegrijpenCodelijst is equal to the default FHIR ValueSet."/>
<binding>
<strength value="required"/>
</binding>
</element>
<element id="Patient.communication:languageControlListening.extension:languageControlListening-proficiency.extension:type">
<path value="Patient.communication.extension.extension"/>
<sliceName value="type"/>
<min value="1"/>
<max value="1"/>
</element>
<element id="Patient.communication:languageControlListening.extension:languageControlListening-proficiency.extension:type.value[x]">
<path value="Patient.communication.extension.extension.value[x]"/>
<patternCoding>
<system value="http://terminology.hl7.org/CodeSystem/v3-LanguageAbilityMode"/>
<code value="RSP"/>
</patternCoding>
<binding>
<strength value="required"/>
</binding>
</element>
</differential>
</StructureDefinition>
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ private IEnumerable<ElementDefinitionNavigator> childDefinitions(string childNam

// Take First(), since we have determined above that there's just one distinct result to expect.
// (this will be the case when Type=R
var expanded = Expand().Single();
var expanded = Expand().Single();
var nav = expanded.Current.ShallowCopy();

if (!nav.MoveToFirstChild()) yield break;
Expand Down Expand Up @@ -233,20 +233,35 @@ public IEnumerable<StructureDefinitionWalker> Resolve()
public StructureDefinitionWalker Extension(string url)
{
// find the extension children of the current node
var extensionChildren = childDefinitions("extension");
var extensionChildren = childDefinitions("extension").ToList();
var selection = extensionChildren.Where(c => isExtensionFor(c, url)).ToList();

// No children in the current profile -> we can still continue in the definition
// of the extension itself.
if (selection.Count == 0)
return FromCanonical(url);
else if (selection.Count == 1)
return new StructureDefinitionWalker(selection.Single(), _resolver);
else
throw new StructureDefinitionWalkerException($"extension('{url}') found multiple extension slices constraining the same extension, with is not allowed for the discriminator at '{Current.CanonicalPath()}'.");
return selection switch
{
// No children in the current profile -> we can still continue in the definition
// of the extension itself.
{ Count: 0 } when isAbsoluteUri(url) => FromCanonical(url),

// No matching child extensions at all -> invalid discriminator.
{ Count: 0 } => throw new StructureDefinitionWalkerException($"extension('{url}') found no extension slices constraining the same extension, with is not allowed for the discriminator at '{Current.CanonicalPath()}'."),

{ Count: 1 } => new StructureDefinitionWalker(selection.Single(), _resolver),

// Too many matching child extensions, no discriminating power -> illegal
_ => throw new StructureDefinitionWalkerException($"extension('{url}') found multiple extension slices constraining the same extension, with is not allowed for the discriminator at '{Current.CanonicalPath()}'.")
};


static bool isExtensionFor(ElementDefinitionNavigator nav, string u)
{
// nav.Current.Type.Any(tr => tr.Code == FHIRAllTypes.Extension.GetLiteral() && tr.Profile == u);
var childNav = nav.ShallowCopy();
if (!childNav.MoveToChild("url")) return false;
return childNav.Current.Fixed is FhirUri fu && fu.Value == u;
}

bool isExtensionFor(ElementDefinitionNavigator nav, string u) =>
nav.Current.Type.Any(tr => tr.Code == FHIRAllTypes.Extension.GetLiteral() && tr.Profile == u);
static bool isAbsoluteUri(string uri) =>
new Uri(uri, UriKind.RelativeOrAbsolute).IsAbsoluteUri;
}
};

Expand Down