Skip to content

Commit 4ff3e31

Browse files
authored
Merge pull request #570 from FirelyTeam/feature/forward-merge-changes
Forward merge changes from SDK5 to SDK6 version
2 parents 0688ada + 3ac8993 commit 4ff3e31

File tree

20 files changed

+484
-117
lines changed

20 files changed

+484
-117
lines changed

.github/dependabot.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,6 @@ updates:
2323
update-types: ["version-update:semver-patch"]
2424
- dependency-name: FluentAssertions
2525
versions: [">=8.0.0"]
26+
- dependency-name: Microsoft.CodeAnalysis.PublicApiAnalyzers
27+
versions: [">=4.14.0"]
2628

firely-validator-api-tests.props

Lines changed: 37 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,44 @@
11
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
22

3-
<PropertyGroup>
4-
<ContainsTests>true</ContainsTests>
5-
<TargetFramework>net8.0</TargetFramework>
6-
<IsPackable>false</IsPackable>
7-
<Nullable>enable</Nullable>
8-
</PropertyGroup>
3+
<PropertyGroup>
4+
<ContainsTests>true</ContainsTests>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<IsPackable>false</IsPackable>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
99

10-
<PropertyGroup>
11-
<FirelySdkVersion>6.0.0-alpha3-20250618.3</FirelySdkVersion>
12-
</PropertyGroup>
10+
<PropertyGroup>
11+
<FirelySdkVersion>6.0.0-beta1</FirelySdkVersion>
12+
</PropertyGroup>
1313

14-
<ItemGroup>
15-
<PackageReference Include="Moq" Version="4.18.4" />
16-
<PackageReference Include="FluentAssertions" Version="7.2.0" />
17-
<PackageReference Include="MessagePack" Version="3.1.3" />
18-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
19-
<PackageReference Include="MSTest.TestFramework" Version="3.8.2" />
20-
<PackageReference Include="MSTest.TestAdapter" Version="3.8.2" />
21-
<PackageReference Include="xunit" Version="2.9.2" />
22-
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.0">
23-
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
24-
<PrivateAssets>all</PrivateAssets>
25-
</PackageReference>
26-
<PackageReference Include="coverlet.collector" Version="6.0.4">
27-
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
28-
<PrivateAssets>all</PrivateAssets>
29-
</PackageReference>
30-
<PackageReference Include="Firely.Fhir.Packages" Version="4.9.1" />
31-
</ItemGroup>
14+
<ItemGroup>
15+
<PackageReference Include="Moq" Version="4.18.4" />
16+
<PackageReference Include="FluentAssertions" Version="7.2.0" />
17+
<PackageReference Include="MessagePack" Version="3.1.4" />
18+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
19+
<PackageReference Include="MSTest.TestFramework" Version="3.8.2" />
20+
<PackageReference Include="MSTest.TestAdapter" Version="3.8.2" />
21+
<PackageReference Include="xunit" Version="2.9.2" />
22+
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.0">
23+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
24+
<PrivateAssets>all</PrivateAssets>
25+
</PackageReference>
26+
<PackageReference Include="coverlet.collector" Version="6.0.4">
27+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
28+
<PrivateAssets>all</PrivateAssets>
29+
</PackageReference>
30+
<PackageReference Include="Firely.Fhir.Packages" Version="4.9.1" />
31+
</ItemGroup>
3232

33-
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
34-
<SignAssembly>True</SignAssembly>
35-
<DelaySign>true</DelaySign>
36-
<AssemblyOriginatorKeyFile>..\..\FirelyValidatorPubKey.snk</AssemblyOriginatorKeyFile>
37-
</PropertyGroup>
33+
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
34+
<SignAssembly>True</SignAssembly>
35+
<DelaySign>true</DelaySign>
36+
<AssemblyOriginatorKeyFile>..\..\FirelyValidatorPubKey.snk</AssemblyOriginatorKeyFile>
37+
</PropertyGroup>
38+
39+
<PropertyGroup>
40+
<NoWarn>ExperimentalApi;SDK0001;</NoWarn>
41+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
42+
</PropertyGroup>
3843

39-
<PropertyGroup>
40-
<NoWarn>ExperimentalApi;SDK0001;</NoWarn>
41-
</PropertyGroup>
42-
4344
</Project>

firely-validator-api.props

Lines changed: 52 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,63 @@
11
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
22

3-
<!-- Solution-wide properties for NuGet packaging -->
4-
<PropertyGroup>
5-
<VersionPrefix>2.7.0</VersionPrefix>
3+
<!-- Solution-wide properties for NuGet packaging -->
4+
<PropertyGroup>
5+
<VersionPrefix>2.7.0</VersionPrefix>
66
<VersionSuffix>alpha</VersionSuffix>
7-
<Authors>Firely</Authors>
8-
<Company>Firely (https://fire.ly)</Company>
9-
<Copyright>Copyright 2015-2025 Firely</Copyright>
10-
<TargetFrameworks>net8.0;netstandard2.1</TargetFrameworks>
11-
<RepositoryType>git</RepositoryType>
12-
<RepositoryUrl>https://github.com/FirelyTeam/firely-validator-api</RepositoryUrl>
13-
<PackageLicenseFile>LICENSE</PackageLicenseFile>
14-
<PackageReadmeFile>README.md</PackageReadmeFile>
15-
</PropertyGroup>
7+
<Authors>Firely</Authors>
8+
<Company>Firely (https://fire.ly)</Company>
9+
<Copyright>Copyright 2015-2025 Firely</Copyright>
10+
<TargetFrameworks>net8.0;netstandard2.1</TargetFrameworks>
11+
<RepositoryType>git</RepositoryType>
12+
<RepositoryUrl>https://github.com/FirelyTeam/firely-validator-api</RepositoryUrl>
13+
<PackageLicenseFile>LICENSE</PackageLicenseFile>
14+
<PackageReadmeFile>README.md</PackageReadmeFile>
15+
</PropertyGroup>
1616

17-
<ItemGroup>
18-
<None Include="..\..\LICENSE" Pack="true" PackagePath=""/>
19-
<None Include="..\..\README.md" Pack="true" PackagePath=""/>
20-
</ItemGroup>
17+
<ItemGroup>
18+
<None Include="..\..\LICENSE" Pack="true" PackagePath=""/>
19+
<None Include="..\..\README.md" Pack="true" PackagePath=""/>
20+
</ItemGroup>
2121

22-
<!-- Compiler settings -->
23-
<PropertyGroup>
24-
<LangVersion>12.0</LangVersion>
25-
<GenerateDocumentationFile>True</GenerateDocumentationFile>
26-
<Nullable>enable</Nullable>
27-
<EnableNETAnalyzers>true</EnableNETAnalyzers>
28-
</PropertyGroup>
22+
<!-- Compiler settings -->
23+
<PropertyGroup>
24+
<LangVersion>12.0</LangVersion>
25+
<GenerateDocumentationFile>True</GenerateDocumentationFile>
26+
<Nullable>enable</Nullable>
27+
<EnableNETAnalyzers>true</EnableNETAnalyzers>
28+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
29+
</PropertyGroup>
2930

30-
<PropertyGroup>
31-
<FirelySdkVersion>6.0.0-alpha3-20250618.3</FirelySdkVersion>
32-
</PropertyGroup>
31+
<PropertyGroup>
32+
<FirelySdkVersion>6.0.0-beta1</FirelySdkVersion>
33+
</PropertyGroup>
3334

34-
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
35-
<DebugType>full</DebugType>
36-
<DebugSymbols>true</DebugSymbols>
37-
<IncludeSymbols>true</IncludeSymbols>
38-
<Optimize>false</Optimize>
39-
<DefineConstants>$(DefineConstants);DEBUG;TRACE</DefineConstants>
40-
<LibraryPKHash></LibraryPKHash>
41-
</PropertyGroup>
35+
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
36+
<DebugType>full</DebugType>
37+
<DebugSymbols>true</DebugSymbols>
38+
<IncludeSymbols>true</IncludeSymbols>
39+
<Optimize>false</Optimize>
40+
<DefineConstants>$(DefineConstants);DEBUG;TRACE</DefineConstants>
41+
<LibraryPKHash></LibraryPKHash>
42+
</PropertyGroup>
4243

43-
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
44-
<IncludeSymbols>True</IncludeSymbols>
45-
<SignAssembly>True</SignAssembly>
46-
<DelaySign>true</DelaySign>
47-
<AssemblyOriginatorKeyFile>..\..\FirelyValidatorPubKey.snk</AssemblyOriginatorKeyFile>
48-
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
49-
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
50-
<LibraryPKHash>0024000004800000940000000602000000240000525341310004000001000100c11eea5df3095844b027f018b356bc326a5a30b1f245010ad789589aa685569b2eb7f5f2ea5c49dafed338e3d9969eab21848c6c20a6b0a22c5ff7797d9a5062d7f3e42478e905d72a3dde344086a003f2df9deeb838e206d92c8cc59150c3151e9490381321f77a716e0a2b24a585b302ba2b3db37966a3da9abe4c601ba4c1</LibraryPKHash>
51-
</PropertyGroup>
44+
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
45+
<IncludeSymbols>True</IncludeSymbols>
46+
<SignAssembly>True</SignAssembly>
47+
<DelaySign>true</DelaySign>
48+
<AssemblyOriginatorKeyFile>..\..\FirelyValidatorPubKey.snk</AssemblyOriginatorKeyFile>
49+
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
50+
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
51+
<LibraryPKHash>0024000004800000940000000602000000240000525341310004000001000100c11eea5df3095844b027f018b356bc326a5a30b1f245010ad789589aa685569b2eb7f5f2ea5c49dafed338e3d9969eab21848c6c20a6b0a22c5ff7797d9a5062d7f3e42478e905d72a3dde344086a003f2df9deeb838e206d92c8cc59150c3151e9490381321f77a716e0a2b24a585b302ba2b3db37966a3da9abe4c601ba4c1</LibraryPKHash>
52+
</PropertyGroup>
5253

53-
<PropertyGroup>
54-
<!-- Error CS4014 Because this call is not awaited, execution of the current method continues before the call is completed.
54+
<PropertyGroup>
55+
<!-- Error CS4014 Because this call is not awaited, execution of the current method continues before the call is completed.
5556
Consider applying the 'await' operator to the result of the call. -->
56-
<WarningsAsErrors>CS4014</WarningsAsErrors>
57-
<WarningsNotAsErrors>NU5104</WarningsNotAsErrors>
58-
<!-- RS0026/RS0027 falsely complains about misuse of optional parameters in the public API-->
59-
<NoWarn>RS0027;ExperimentalApi;SDK0001</NoWarn>
60-
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
61-
</PropertyGroup>
57+
<WarningsAsErrors>CS4014</WarningsAsErrors>
58+
<WarningsNotAsErrors>NU5104</WarningsNotAsErrors>
59+
<!-- RS0026/RS0027 falsely complains about misuse of optional parameters in the public API-->
60+
<NoWarn>RS0027;ExperimentalApi;SDK0001</NoWarn>
61+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
62+
</PropertyGroup>
6263
</Project>

src/Firely.Fhir.Validation.Shared/Validator.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ public Validator(
5050
_settings.ValidateCodeService = terminologyService;
5151

5252
_settings.ResolveExternalReference = referenceResolver is not null ? resolve : null;
53+
_settings.ConformanceResourceResolver ??= resourceResolver;
5354

5455
PocoNode? resolve(string reference, string location)
5556
{

src/Firely.Fhir.Validation/Firely.Fhir.Validation.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
</PropertyGroup>
1111

1212
<ItemGroup>
13-
<PackageReference Include="MessagePack.Annotations" Version="3.1.3" />
13+
<PackageReference Include="MessagePack.Annotations" Version="3.1.4" />
1414
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
1515
<PackageReference Include="System.ValueTuple" Version="4.6.1" />
1616
<PackageReference Include="Hl7.Fhir.Base" Version="$(FirelySdkVersion)" />
@@ -19,6 +19,7 @@
1919
<InternalsVisibleTo Include="Firely.Fhir.Validation.R4B" Key="$(LibraryPKHash)" />
2020
<InternalsVisibleTo Include="Firely.Fhir.Validation.R5" Key="$(LibraryPKHash)" />
2121
<InternalsVisibleTo Include="Firely.Fhir.Validation.STU3" Key="$(LibraryPKHash)" />
22+
<InternalsVisibleTo Include="Firely.Fhir.Validation.Enterprise" Key="$(LibraryPKHash)" />
2223
<InternalsVisibleTo Include="Firely.Fhir.Validation.Compilation.Enterprise.R4" Key="$(LibraryPKHash)" />
2324
<InternalsVisibleTo Include="Firely.Fhir.Validation.Compilation.Enterprise.R4B" Key="$(LibraryPKHash)" />
2425
<InternalsVisibleTo Include="Firely.Fhir.Validation.Compilation.Enterprise.R5" Key="$(LibraryPKHash)" />

src/Firely.Fhir.Validation/Impl/ChildrenValidator.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ ResultReport IValidatable.Validate(PocoNode input, ValidationSettings vc, Valida
9595
// Listing children can be an expensive operation, so make sure we run it once.
9696
var elementsToMatch = input.Children().ToList();
9797

98-
var matchResult = ChildNameMatcher.Match(ChildList, elementsToMatch);
98+
var matchResult = ChildNameMatcher.Match(input, ChildList, elementsToMatch);
9999
if (matchResult.UnmatchedInstanceElements?.Count > 0 && !AllowAdditionalChildren)
100100
{
101101
var elementList = string.Join(",", matchResult.UnmatchedInstanceElements.Select(e => $"'{e.Name}'"));
@@ -153,7 +153,7 @@ public IAssertion this[string key]
153153

154154
internal class ChildNameMatcher
155155
{
156-
public static MatchResult Match(IReadOnlyDictionary<string, IAssertion> assertions, IEnumerable<PocoNodeOrList> children)
156+
public static MatchResult Match(PocoNode input, IReadOnlyDictionary<string, IAssertion> assertions, IEnumerable<PocoNodeOrList> children)
157157
{
158158
var elementsToMatch = children.ToList();
159159

@@ -170,7 +170,7 @@ public static MatchResult Match(IReadOnlyDictionary<string, IAssertion> assertio
170170

171171
Match match = found.Any()
172172
? new(assertion.Key, assertion.Value, found.Count == 1 ? found.First() : throw new ArgumentException("Multiple elements found for child assertion."))
173-
: new(assertion.Key, assertion.Value, null);
173+
: new(assertion.Key, assertion.Value, new PocoListNode([], input, assertion.Key));
174174
elementsToMatch.RemoveAll(e => found.Contains(e));
175175

176176
matches.Add(match);

src/Firely.Fhir.Validation/Impl/ElementSchema.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,16 @@ internal virtual ResultReport ValidateInternal(
7171
ValidationSettings vc,
7272
ValidationState state)
7373
{
74-
// If there is no input, just run the cardinality checks, nothing else - essential to keep validation performance high.
75-
if (!input.Any()) return ResultReport.SUCCESS;
74+
// If there is no input, just run the slice validators, nothing else - essential to keep validation performance high.
75+
if (!input.Any())
76+
{
77+
var sliceValidators = Members.OfType<SliceValidator>().Where(vc.Filter);
78+
// Run slice validators for empty input - they need to validate mandatory slices
79+
// This fixes GitHub issue #544 where empty extensions with mandatory slices were not being validated
80+
var sliceResults = sliceValidators.Select(sv => ((IGroupValidatable)sv).Validate(input, vc, state)).ToList();
81+
return ResultReport.Combine(sliceResults);
82+
83+
}
7684

7785
var members = Members.Where(vc.Filter);
7886
var subresult = members.Select(ma => ma.ValidateMany(input, vc, state));

src/Firely.Fhir.Validation/Impl/ResourceSchema.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,14 @@ public ResourceSchema(StructureDefinitionInformation structureDefinition, IEnume
4949
/// <summary>
5050
/// Gets the canonical of the profile(s) referred to in the <c>Meta.profile</c> property of the resource.
5151
/// </summary>
52-
internal static Canonical[] GetMetaProfileSchemas(PocoNode instance, MetaProfileSelector? selector, ValidationState state)
52+
internal static Canonical[] GetMetaProfileSchemas(PocoNode instance, ValidationSettings vc, ValidationState state)
5353
{
5454
var profiles = instance.NavigateTo("meta.profile").Select(pn => new Canonical((string)pn.GetValue()!)).ToArray();
5555

56-
return callback(selector).Invoke(instance.GetLocation(), profiles);
56+
return callback(vc.SelectValidationProfiles).Invoke(instance.GetLocation(), profiles, instance, vc);
5757

58-
static MetaProfileSelector callback(MetaProfileSelector? selector)
59-
=> selector ?? ((_, m) => m);
58+
static ValidationProfileSelector callback(ValidationProfileSelector? selector)
59+
=> selector ?? ((_, m, _, _) => m);
6060
}
6161

6262
/// <inheritdoc />
@@ -86,7 +86,7 @@ internal override ResultReport ValidateInternal(PocoNode input, ValidationSettin
8686
// FHIR has a few occasions where the schema needs to read into the instance to obtain additional schemas to
8787
// validate against (Resource.meta.profile, Extension.url). Fetch these from the instance and combine them into
8888
// a coherent set to validate against.
89-
var additionalCanonicals = GetMetaProfileSchemas(input, vc.SelectMetaProfiles, state);
89+
var additionalCanonicals = GetMetaProfileSchemas(input, vc, state);
9090

9191
if (additionalCanonicals.Any() && vc.ElementSchemaResolver is null)
9292
throw new ArgumentException($"Cannot validate profiles in meta.profile because {nameof(ValidationSettings)} does not contain an ElementSchemaResolver.");

src/Firely.Fhir.Validation/Impl/SliceValidator.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -149,10 +149,11 @@ ResultReport IGroupValidatable.Validate(IEnumerable<PocoNode> input, ValidationS
149149
var defaultInUse = false;
150150
List<ResultReport> evidence = new();
151151
var buckets = new Buckets(Slices, Default);
152-
var sliceLocation = input.First().GetLocation();
152+
var slices = input.ToArray();
153+
var sliceLocation = slices.FirstOrDefault()?.GetLocation();
153154

154155
// Go over the elements in the instance, in order
155-
foreach (var candidate in input)
156+
foreach (var candidate in slices)
156157
{
157158
bool hasSucceeded = false;
158159

@@ -170,7 +171,7 @@ ResultReport IGroupValidatable.Validate(IEnumerable<PocoNode> input, ValidationS
170171
// this is not allowed
171172
if (sliceNumber < lastMatchingSlice && Ordered)
172173
evidence.Add(new IssueAssertion(Issue.CONTENT_ELEMENT_SLICING_OUT_OF_ORDER,
173-
$"Element matches slice {sliceLocation}:{sliceName}', but this is out of order for group {sliceLocation}, since a previous element already matched slice '{sliceLocation}:{Slices[lastMatchingSlice].Name}'")
174+
$"Element matches slice {sliceLocation!}:{sliceName}', but this is out of order for group {sliceLocation!}, since a previous element already matched slice '{sliceLocation!}:{Slices[lastMatchingSlice].Name}'")
174175
.AsResult(state, candidate, nameof(SliceValidator)));
175176
else
176177
lastMatchingSlice = sliceNumber;
@@ -179,7 +180,7 @@ ResultReport IGroupValidatable.Validate(IEnumerable<PocoNode> input, ValidationS
179180
{
180181
// We found a match while we already added a non-match to a "open at end" slicegroup, that's not allowed
181182
evidence.Add(new IssueAssertion(Issue.CONTENT_ELEMENT_FAILS_SLICING_RULE,
182-
$"Element matched slice '{sliceLocation}:{sliceName}', but it appears after a non-match, which is not allowed for an open-at-end group")
183+
$"Element matched slice '{sliceLocation!}:{sliceName}', but it appears after a non-match, which is not allowed for an open-at-end group")
183184
.AsResult(state, candidate, nameof(SliceValidator)));
184185
}
185186

@@ -207,10 +208,11 @@ ResultReport IGroupValidatable.Validate(IEnumerable<PocoNode> input, ValidationS
207208
}
208209
}
209210

211+
var pn = input as PocoNodeOrList ?? slices.First();
210212
evidence.AddRange(buckets
211213
.Where(slice => slice.Value is null && slice.Key.Required)
212-
.Select(slice => new IssueAssertion(Issue.CONTENT_INCORRECT_OCCURRENCE, $"No elements matched required slice: '{input.First().Name}:{slice.Key.Name}'")
213-
.AsResult(state, input.First().Parent!, nameof(SliceValidator))));
214+
.Select(slice => new IssueAssertion(Issue.CONTENT_INCORRECT_OCCURRENCE, $"No elements matched required slice: '{pn.Name}:{slice.Key.Name}'")
215+
.AsResult(state, pn.Parent!, nameof (SliceValidator))));
214216
evidence.AddRange(buckets.Validate(vc, state));
215217

216218
return ResultReport.Combine(evidence);

0 commit comments

Comments
 (0)