Skip to content

Commit

Permalink
Fix: better process the elements on structures that do not have snaps…
Browse files Browse the repository at this point in the history
…hots.

Update R4+ Info files with improvements (only affects profiles).
Update R4 OpenAPI files with additional sorting.
  • Loading branch information
GinoCanessa committed Sep 6, 2024
1 parent 97b8f27 commit c87be69
Show file tree
Hide file tree
Showing 8 changed files with 35,075 additions and 34,936 deletions.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Diagnostics.CodeAnalysis;
using Hl7.Fhir.Model;
using Hl7.Fhir.Specification.Navigation;
using Hl7.Fhir.Utility;
using Microsoft.Health.Fhir.CodeGen.Models;
using Microsoft.Health.Fhir.CodeGenCommon.Extensions;
using Microsoft.Health.Fhir.CodeGenCommon.FhirExtensions;
Expand All @@ -17,6 +18,59 @@ namespace Microsoft.Health.Fhir.CodeGen.FhirExtensions;

public static class StructureDefinitionExtensions
{
/// <summary>
/// Represents the processing information for a StructureDefinition.
/// </summary>
public record class StructureProcessingInfo
{
/// <summary>
/// Gets or sets the artifact class.
/// </summary>
public required FhirArtifactClassEnum ArtifactClass { get; init; }

/// <summary>
/// Gets or sets a flag indicating if the snapshot has been processed.
/// </summary>
public required bool HasProcessedSnapshot { get; init; }

/// <summary>
/// Gets or sets a flag indicating if the differential has been processed.
/// </summary>
public required bool HasProcessedDifferential { get; init; }
}

/// <summary>
/// Sets the processing information for the specified StructureDefinition.
/// </summary>
/// <param name="sd">The StructureDefinition.</param>
/// <param name="spi">The StructureProcessingInfo.</param>
public static void cgSetProcessingInfo(
this StructureDefinition sd,
StructureProcessingInfo spi)
{
if (sd.HasAnnotation<StructureProcessingInfo>())
{
sd.RemoveAnnotations<StructureProcessingInfo>();
}

sd.AddAnnotation(spi);
}

/// <summary>
/// Gets the processing information for the specified StructureDefinition.
/// </summary>
/// <param name="sd">The StructureDefinition.</param>
/// <returns>The StructureProcessingInfo if available, otherwise null.</returns>
public static StructureProcessingInfo? cgGetProcessingInfo(this StructureDefinition sd)
{
if (sd.TryGetAnnotation(out StructureProcessingInfo? spi))
{
return spi;
}

return null;
}

/// <summary>A StructureDefinition extension method that cg artifact class.</summary>
/// <param name="sd">The SD to act on.</param>
/// <returns>A FhirArtifactClassEnum.</returns>
Expand Down
130 changes: 112 additions & 18 deletions src/Microsoft.Health.Fhir.CodeGen/Models/DefinitionCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
using Microsoft.Health.Fhir.CodeGenCommon.Packaging;

using Microsoft.Health.Fhir.CodeGenCommon.Polyfill;
using static Microsoft.Health.Fhir.CodeGen.FhirExtensions.StructureDefinitionExtensions;

namespace Microsoft.Health.Fhir.CodeGen.Models;

Expand Down Expand Up @@ -145,15 +146,17 @@ public async System.Threading.Tasks.Task<bool> TryGenerateMissingSnapshots()
bool success = true;
Hl7.Fhir.Specification.Snapshot.SnapshotGenerator snapshotGenerator = new(this);

await CreateSnapshots(_complexTypesByName.Values);
await CreateSnapshots(_resourcesByName.Values);
await CreateSnapshots(_interfacesByName.Values);
await CreateSnapshots(_extensionsByUrl.Values);
await CreateSnapshots(_profilesByUrl.Values);
await CreateSnapshots(FhirArtifactClassEnum.ComplexType, _complexTypesByName.Values);
await CreateSnapshots(FhirArtifactClassEnum.Resource, _resourcesByName.Values);
await CreateSnapshots(FhirArtifactClassEnum.Interface, _interfacesByName.Values);
await CreateSnapshots(FhirArtifactClassEnum.Extension, _extensionsByUrl.Values);
await CreateSnapshots(FhirArtifactClassEnum.Profile, _profilesByUrl.Values);

return success;

async System.Threading.Tasks.Task CreateSnapshots(IEnumerable<StructureDefinition> sds)
async System.Threading.Tasks.Task CreateSnapshots(
FhirArtifactClassEnum artifactClass,
IEnumerable<StructureDefinition> sds)
{
foreach (StructureDefinition sd in sds)
{
Expand All @@ -175,7 +178,11 @@ async System.Threading.Tasks.Task CreateSnapshots(IEnumerable<StructureDefinitio
{
success = false;
Console.WriteLine($"Failed to generate snapshot for {sd.Url} ({sd.Name})");
continue;
}

// reprocess the elements for this structure so that we have the snapshot
ProcessElements(artifactClass, sd, FhirSequence);
}
}
}
Expand Down Expand Up @@ -356,10 +363,22 @@ private void ProcessElements(FhirArtifactClassEnum artifactClass, StructureDefin

List<string> idByDepth = [];

Dictionary<string, string> pathTypes = [];
HashSet<string> multiTypePaths = [];
Dictionary<string, string> singleTypePaths = [];

StructureProcessingInfo processingInfo = sd.cgGetProcessingInfo() ?? new()
{
ArtifactClass = artifactClass,
HasProcessedSnapshot = false,
HasProcessedDifferential = false,
};

IEnumerable<ElementDefinition> elements = processingInfo.HasProcessedSnapshot
? []
: sd.Snapshot?.Element ?? [];

// process each element in the snapshot
foreach (ElementDefinition ed in sd.Snapshot?.Element ?? Enumerable.Empty<ElementDefinition>())
foreach (ElementDefinition ed in elements)
{
int lastDot = ed.Path.LastIndexOf('.');
string parentPath = lastDot == -1 ? ed.Path : ed.Path.Substring(0, lastDot);
Expand Down Expand Up @@ -390,7 +409,7 @@ private void ProcessElements(FhirArtifactClassEnum artifactClass, StructureDefin
if (parentPath.Contains('.') &&
!_parentElementsAndType.ContainsKey(parentPath))
{
if (pathTypes.TryGetValue(parentPath, out string? parentType))
if (singleTypePaths.TryGetValue(parentPath, out string? parentType))
{
_parentElementsAndType.Add(parentPath, parentType);
}
Expand All @@ -411,10 +430,10 @@ private void ProcessElements(FhirArtifactClassEnum artifactClass, StructureDefin
}
else
{
//if (!slices.Any(sliceDef => sliceDef.Key == ed.SliceName))
//{
if (!slices.Any(sliceDef => (sliceDef.Key == ed.SliceName) && (sliceDef.Value.Id == sd.Id)))
{
_pathsWithSlices[ed.Path] = [.. slices, new(ed.SliceName, sd)];
//}
}
}
}

Expand All @@ -424,12 +443,53 @@ private void ProcessElements(FhirArtifactClassEnum artifactClass, StructureDefin
// check for a single type and add to the path types dictionary
if (ed.Type.Count == 1)
{
pathTypes[ed.Path] = ed.Type.First().Code;
// check for existing multi-type path
if (multiTypePaths.Contains(ed.Path))
{
// do nothing
}
// check to see if there is an existing type for this path
else if (singleTypePaths.TryGetValue(ed.Path, out string? existingType))
{
// if the existing type is not the same as the current type, it is a polymorphic type
if (existingType != ed.Type.First().Code)
{
// remove from the set
singleTypePaths.Remove(ed.Path);
multiTypePaths.Add(ed.Path);
}
}
else
{
// add this type
singleTypePaths[ed.Path] = ed.Type.First().Code;
}
}
else
{
// this is a multi-type path
_ = multiTypePaths.Add(ed.Path);
}
}

// check to see if we processed a snapshot
if (allFieldOrders.Count != 0)
{
processingInfo = processingInfo with { HasProcessedSnapshot = true };

// if we just processed the snapshot, process the differential even if it has been processed before
elements = sd.Differential?.Element ?? Enumerable.Empty<ElementDefinition>();
}
else
{
// only process the differential if we have one that has not been processed
elements = processingInfo.HasProcessedDifferential
? []
: sd.Differential?.Element ?? [];
}

// process each element in the differential
foreach (ElementDefinition ed in sd.Differential?.Element ?? Enumerable.Empty<ElementDefinition>())
foreach (ElementDefinition ed in elements)
{
int lastDot = ed.Path.LastIndexOf('.');
string parentPath = lastDot == -1 ? ed.Path : ed.Path.Substring(0, lastDot);
Expand All @@ -450,7 +510,7 @@ private void ProcessElements(FhirArtifactClassEnum artifactClass, StructureDefin
}
}

// check if this element has already been processed
// check if this element NOT has already been processed in the snapshot
if (!allFieldOrders.TryGetValue(ed.ElementId, out int fo))
{
fo = allFieldOrders.Count;
Expand All @@ -462,7 +522,7 @@ private void ProcessElements(FhirArtifactClassEnum artifactClass, StructureDefin
if (parentPath.Contains('.') &&
!_parentElementsAndType.ContainsKey(parentPath))
{
if (pathTypes.TryGetValue(parentPath, out string? parentType))
if (singleTypePaths.TryGetValue(parentPath, out string? parentType))
{
_parentElementsAndType.Add(parentPath, parentType);
}
Expand All @@ -483,7 +543,7 @@ private void ProcessElements(FhirArtifactClassEnum artifactClass, StructureDefin
}
else
{
if (!slices.Any(sliceDef => sliceDef.Key == ed.SliceName))
if (!slices.Any(sliceDef => (sliceDef.Key == ed.SliceName) && (sliceDef.Value.Id == sd.Id)))
{
_pathsWithSlices[ed.Path] = [.. slices, new(ed.SliceName, sd)];
}
Expand All @@ -496,12 +556,46 @@ private void ProcessElements(FhirArtifactClassEnum artifactClass, StructureDefin
// check for a single type and add to the path types dictionary
if (ed.Type.Count == 1)
{
pathTypes[ed.Path] = ed.Type.First().Code;
// check for existing multi-type path
if (multiTypePaths.Contains(ed.Path))
{
// do nothing
}
// check to see if there is an existing type for this path
else if (singleTypePaths.TryGetValue(ed.Path, out string? existingType))
{
// if the existing type is not the same as the current type, it is a polymorphic type
if (existingType != ed.Type.First().Code)
{
// remove from the set
singleTypePaths.Remove(ed.Path);
multiTypePaths.Add(ed.Path);
}
}
else
{
// add this type
singleTypePaths[ed.Path] = ed.Type.First().Code;
}
}
else
{
// this is a multi-type path
_ = multiTypePaths.Add(ed.Path);
}
}

ed.cgSetFieldOrder(fo, componentFieldOrder);
}

// check to see if we processed a differential
if (sd.Differential?.Element.Count > 0)
{
processingInfo = processingInfo with { HasProcessedDifferential = true };
}

// update our processing info
sd.cgSetProcessingInfo(processingInfo);
}

/// <summary>
Expand Down

0 comments on commit c87be69

Please sign in to comment.