Skip to content

Commit 649bacb

Browse files
authored
Merge pull request #3274 from FirelyTeam/fix/3177-stu3
Also erase contentRef in STU3
2 parents 3a3ae14 + bb1ef9b commit 649bacb

File tree

3 files changed

+167
-56
lines changed

3 files changed

+167
-56
lines changed

src/Hl7.Fhir.STU3/Specification/Snapshot/SnapshotGenerator.cs

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,12 @@ private async Tasks.Task<bool> expandElement(ElementDefinitionNavigator nav)
539539

540540
// [WMR 20190926] #1123 Remove annotations and fix Base components!
541541
SnapshotGenerator.copyChildren(nav, sourceNav);
542+
543+
// [EK 20250618] #3177 Ensure we don't have both children and a contentReference.
544+
// We should restore the Type, since that's expected information if there is no
545+
// content reference available.
546+
defn.ContentReference = null;
547+
defn.Type = sourceNav.Current.Type.DeepCopy().ToList();
542548

543549
// [WMR 20180410]
544550
// - Regenerate element IDs
@@ -1262,35 +1268,35 @@ private static void fixExtensionAnnotationsAfterMerge(ElementDefinition elem)
12621268
}
12631269

12641270
/// <summary>
1265-
/// Copy child elements from <paramref name="typeNav"/> to <paramref name="nav"/>.
1271+
/// Copy child elements from <paramref name="source"/> to <paramref name="dest"/>.
12661272
/// Remove existing annotations, fix Base components
12671273
/// </summary>
12681274
// [WMR 20170501] OBSOLETE: notify listeners - moved to prepareTypeProfileChildren
1269-
private static bool copyChildren(ElementDefinitionNavigator nav, ElementDefinitionNavigator typeNav) // , StructureDefinition typeStructure)
1275+
private static bool copyChildren(ElementDefinitionNavigator dest, ElementDefinitionNavigator source) // , StructureDefinition typeStructure)
12701276
{
12711277
// [WMR 20170426] IMPORTANT!
12721278
// Do NOT modify typeNav/typeStructure
12731279
// Call by mergeTypeProfiles: typeNav/typeStructure refers to modified clone of global type profile
12741280
// Call by expandElement: typeNav/typeStructure refers to global cached type profile (!)
12751281

1276-
Debug.Assert(!nav.AtRoot);
1277-
Debug.Assert(!typeNav.AtRoot);
1282+
Debug.Assert(!dest.AtRoot);
1283+
Debug.Assert(!source.AtRoot);
12781284

12791285
// [WMR 20170220] CopyChildren returns false if nav already has children
1280-
if (nav.CopyChildren(typeNav))
1286+
if (dest.CopyChildren(source))
12811287
{
12821288
// Fix the copied elements and notify observers
12831289

12841290
// [WMR 20190926] Also support contentReference
12851291
// typeNav positioned at target element of base profile (not the root element)
12861292
// => process only the current subtree, not the full structure
12871293

1288-
var typeRootPath = typeNav.Path;
1289-
var typeRootPos = typeNav.OrdinalPosition.Value; // 0 for element type, >0 for content reference
1290-
var typeElems = typeNav.Elements;
1291-
var elems = nav.Elements;
1294+
var typeRootPath = source.Path;
1295+
var typeRootPos = source.OrdinalPosition.Value; // 0 for element type, >0 for content reference
1296+
var typeElems = source.Elements;
1297+
var elems = dest.Elements;
12921298

1293-
for (int pos = nav.OrdinalPosition.Value + 1, i = typeRootPos + 1;
1299+
for (int pos = dest.OrdinalPosition.Value + 1, i = typeRootPos + 1;
12941300
i < typeElems.Count && pos < elems.Count;
12951301
i++, pos++)
12961302
{

src/Hl7.Fhir.Specification.STU3.Tests/Snapshot/SnapshotGeneratorTest.cs

Lines changed: 119 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7669,14 +7669,12 @@ public async Tasks.Task TestAbsoluteContentReferenceGeneration()
76697669
var zipSource = ZipSource.CreateValidationSource();
76707670
var generator = new SnapshotGenerator(zipSource, SnapshotGeneratorSettings.CreateDefault());
76717671

7672-
76737672
//Test if core resource has relative content references.
76747673
var coreQuestionnaire = await _testResolver.FindStructureDefinitionAsync("http://hl7.org/fhir/StructureDefinition/Questionnaire");
76757674
var coreSnapshot = await generator.GenerateAsync(coreQuestionnaire);
76767675
var item = coreSnapshot.Where(e => e.Path == "Questionnaire.item.item").FirstOrDefault();
76777676
item.ContentReference.Should().Be("#Questionnaire.item");
76787677

7679-
76807678
//Create profile for testing creation of absolute references.
76817679
var profile = new StructureDefinition
76827680
{
@@ -7718,15 +7716,9 @@ public async Tasks.Task TestAbsoluteContentReferenceGeneration()
77187716
},
77197717
new ElementDefinition
77207718
{
7721-
ElementId = "Questionnaire.item:booleanItem.type",
7722-
Path = "Questionnaire.item.type",
7723-
Fixed = new Code("boolean")
7724-
},
7725-
new ElementDefinition
7726-
{
7727-
ElementId = "Questionnaire.item:booleanItem.item.type",
7728-
Path = "Questionnaire.item.item.type",
7729-
Fixed = new Code("string")
7719+
ElementId = "Questionnaire.item:booleanItem.item",
7720+
Path = "Questionnaire.item.item",
7721+
Min = 1
77307722
}
77317723
}
77327724
}
@@ -7737,9 +7729,6 @@ public async Tasks.Task TestAbsoluteContentReferenceGeneration()
77377729

77387730
var cref1 = profileSnapshot.Where(e => e.ElementId == "Questionnaire.item:booleanItem.item").FirstOrDefault();
77397731
cref1.ContentReference.Should().Be("http://hl7.org/fhir/StructureDefinition/Questionnaire#Questionnaire.item");
7740-
7741-
var cref2 = profileSnapshot.Where(e => e.ElementId == "Questionnaire.item:booleanItem.item.item").FirstOrDefault();
7742-
cref2.ContentReference.Should().Be("http://hl7.org/fhir/StructureDefinition/Questionnaire#Questionnaire.item");
77437732
}
77447733

77457734
[TestMethod]
@@ -8460,5 +8449,121 @@ public async Tasks.Task TestMergingAPreviouslyRemovedElement()
84608449
Assert.IsNull(valueQuantityEld);
84618450

84628451
}
8452+
8453+
// Test whether we have fixed issue https://github.com/FirelyTeam/firely-net-sdk/issues/3177.
8454+
[TestMethod]
8455+
public async Tasks.Task TestSliceWithContentReference()
8456+
{
8457+
var sd = buildSliceOnContentReference();
8458+
_generator = new SnapshotGenerator(_testResolver, _settings);
8459+
var snapshot = await _generator.GenerateAsync(sd);
8460+
8461+
// If we have copied the contentReference's children to the slice, there should not be a contentReference
8462+
// on the slice itself anymore (but it should still exist on the intro).
8463+
snapshot.Single(e => e.ElementId == "Parameters.parameter.part")
8464+
.ContentReference.Should().NotBeNull();
8465+
snapshot.Should().ContainSingle(e => e.ElementId == "Parameters.parameter.part:medicationDispense.name");
8466+
8467+
// The slice itself should not have a contentReference, because it is copied the children below it.
8468+
var firstSlice = snapshot.Single(e => e.ElementId == "Parameters.parameter.part:medicationDispense");
8469+
firstSlice.ContentReference.Should().BeNull();
8470+
8471+
// But it should now have a TypeRef element!
8472+
firstSlice.Type.Should().ContainSingle(tr => tr.Code == "BackboneElement");
8473+
}
8474+
8475+
// Test whether fixing issue https://github.com/FirelyTeam/firely-net-sdk/issues/3177 does
8476+
// not break the snapshot generation when just the cardinality of the contentReference is changed.
8477+
[TestMethod]
8478+
public async Tasks.Task TestContentReferenceWithCardinalityChangeOnPart()
8479+
{
8480+
var sd = changeCardinalityOnContentReference();
8481+
_generator = new SnapshotGenerator(_testResolver, _settings);
8482+
var snapshot = await _generator.GenerateAsync(sd);
8483+
8484+
// Changing the cardinality will not copy the children, so the contentReference should still
8485+
// be there and NOT have a typeref.
8486+
var firstPart = snapshot.Single(e => e.ElementId == "Parameters.parameter.part");
8487+
firstPart.ContentReference.Should().NotBeNull();
8488+
firstPart.Type.Should().BeEmpty();
8489+
}
8490+
8491+
// Test whether fixing issue https://github.com/FirelyTeam/firely-net-sdk/issues/3177 does
8492+
// not break the snapshot generation when just the cardinality of a nested contentReference is changed.
8493+
[TestMethod]
8494+
public async Tasks.Task TestContentReferenceWithCardinalityChangeOnNestedPart()
8495+
{
8496+
var sd = changeCardinalityOnNestedContentReference();
8497+
_generator = new SnapshotGenerator(_testResolver, _settings);
8498+
var snapshot = await _generator.GenerateAsync(sd);
8499+
8500+
// Changing the cardinality in a child *will* copy the children, so the contentReference should
8501+
// now be gone, and it should have a TypeRef instead.
8502+
var firstPart = snapshot.Single(e => e.ElementId == "Parameters.parameter.part");
8503+
firstPart.ContentReference.Should().BeNull();
8504+
firstPart.Type.Should().ContainSingle(tr => tr.Code == "BackboneElement");
8505+
8506+
// But the nested part should still have a contentReference, and no typeref.
8507+
var nestedPart = snapshot.Single(e => e.ElementId == "Parameters.parameter.part.part");
8508+
nestedPart.ContentReference.Should().NotBeNull();
8509+
nestedPart.Type.Should().BeEmpty();
8510+
}
8511+
8512+
private static StructureDefinition buildSliceOnContentReference()
8513+
{
8514+
var result = TestProfileArtifactSource.CreateTestSD("http://validationtest.org/fhir/StructureDefinition/Parameters-issue-3177", "Parameters-issue-3177",
8515+
"Parameters with sliced parts - and so copied contentReferences", FHIRAllTypes.Parameters);
8516+
8517+
var cons = result.Differential.Element;
8518+
8519+
var slicingIntro = new ElementDefinition("Parameters.parameter.part")
8520+
.WithSlicingIntro(ElementDefinition.SlicingRules.Closed,
8521+
(ElementDefinition.DiscriminatorType.Pattern, "name"))
8522+
.Required();
8523+
8524+
cons.Add(slicingIntro);
8525+
8526+
cons.Add(new ElementDefinition("Parameters.parameter.part")
8527+
{
8528+
ElementId = "Parameters.parameter.part:medicationDispense", SliceName = "medicationDispense",
8529+
}.Required());
8530+
8531+
cons.Add(new ElementDefinition("Parameters.parameter.part.name")
8532+
{
8533+
ElementId = "Parameters.parameter.part:medicationDispense.name",
8534+
Pattern = new FhirString("medicationDispense")
8535+
});
8536+
8537+
return result;
8538+
}
8539+
8540+
private static StructureDefinition changeCardinalityOnContentReference()
8541+
{
8542+
var result = TestProfileArtifactSource.CreateTestSD("http://validationtest.org/fhir/StructureDefinition/Parameters-issue-3177", "Parameters-issue-3177",
8543+
"Parameters with new cardinality on parts", FHIRAllTypes.Parameters);
8544+
8545+
var cons = result.Differential.Element;
8546+
8547+
var mainPart = new ElementDefinition("Parameters.parameter.part")
8548+
.Required();
8549+
8550+
cons.Add(mainPart);
8551+
8552+
return result;
8553+
}
8554+
8555+
private static StructureDefinition changeCardinalityOnNestedContentReference()
8556+
{
8557+
var result = TestProfileArtifactSource.CreateTestSD("http://validationtest.org/fhir/StructureDefinition/Parameters-issue-3177", "Parameters-issue-3177",
8558+
"Parameters with new cardinality on part.part", FHIRAllTypes.Parameters);
8559+
8560+
var cons = result.Differential.Element;
8561+
8562+
var nestedPart = new ElementDefinition("Parameters.parameter.part.part")
8563+
.Required();
8564+
8565+
cons.Add(nestedPart);
8566+
8567+
return result; }
84638568
}
84648569
}

0 commit comments

Comments
 (0)