From df4cff70fc5539906b9730817d9148b95a76fef7 Mon Sep 17 00:00:00 2001 From: Marco Visser Date: Tue, 19 Dec 2023 11:30:05 +0100 Subject: [PATCH] Make IElementSchemaResolver public --- .../Impl/DatatypeSchema.cs | 11 +++--- .../Impl/ElementSchema.cs | 38 +++++++++++-------- .../Impl/ExtensionSchema.cs | 9 ++--- src/Firely.Fhir.Validation/Impl/FhirSchema.cs | 9 ++--- .../Impl/ResourceSchema.cs | 14 +++---- .../Impl/SchemaReferenceValidator.cs | 3 +- .../PublicAPI.Unshipped.txt | 4 ++ .../Schema/IElementSchemaResolver.cs | 2 +- .../ResourceSchemaValidationTests.cs | 2 +- 9 files changed, 48 insertions(+), 44 deletions(-) diff --git a/src/Firely.Fhir.Validation/Impl/DatatypeSchema.cs b/src/Firely.Fhir.Validation/Impl/DatatypeSchema.cs index 208e167e..18d5ead6 100644 --- a/src/Firely.Fhir.Validation/Impl/DatatypeSchema.cs +++ b/src/Firely.Fhir.Validation/Impl/DatatypeSchema.cs @@ -4,7 +4,6 @@ * via any medium is strictly prohibited. */ -using Hl7.Fhir.ElementModel; using System; using System.Collections.Generic; using System.Linq; @@ -35,16 +34,16 @@ public DatatypeSchema(StructureDefinitionInformation structureDefinition, IEnume } /// - public override ResultReport Validate(IEnumerable input, ValidationContext vc, ValidationState state) + internal override ResultReport ValidateInternal(IEnumerable input, ValidationContext vc, ValidationState state) { // Schemas representing the root of a FHIR datatype cannot meaningfully be used as a GroupValidatable, // so we'll turn this into a normal IValidatable. - var results = input.Select((i, index) => Validate(i, vc, state.UpdateInstanceLocation(d => d.ToIndex(index)))); + var results = input.Select((i, index) => ValidateInternal(i, vc, state.UpdateInstanceLocation(d => d.ToIndex(index)))); return ResultReport.Combine(results.ToList()); } /// - public override ResultReport Validate(IScopedNode input, ValidationContext vc, ValidationState state) + internal override ResultReport ValidateInternal(IScopedNode input, ValidationContext vc, ValidationState state) { // FHIR specific rule about dealing with abstract datatypes (not profiles!): if this schema is an abstract datatype, // we need to run validation against the schema for the actual type, not the abstract type. @@ -55,10 +54,10 @@ public override ResultReport Validate(IScopedNode input, ValidationContext vc, V var typeProfile = vc.TypeNameMapper.MapTypeName(input.InstanceType); var fetchResult = FhirSchemaGroupAnalyzer.FetchSchema(vc.ElementSchemaResolver, state, typeProfile); - return fetchResult.Success ? fetchResult.Schema!.Validate(input, vc, state) : fetchResult.Error!; + return fetchResult.Success ? fetchResult.Schema!.ValidateInternal(input, vc, state) : fetchResult.Error!; } else - return base.Validate(input, vc, state); + return base.ValidateInternal(input, vc, state); } diff --git a/src/Firely.Fhir.Validation/Impl/ElementSchema.cs b/src/Firely.Fhir.Validation/Impl/ElementSchema.cs index 5fa16c91..c557d3b7 100644 --- a/src/Firely.Fhir.Validation/Impl/ElementSchema.cs +++ b/src/Firely.Fhir.Validation/Impl/ElementSchema.cs @@ -4,7 +4,6 @@ * via any medium is strictly prohibited. */ -using Hl7.Fhir.ElementModel; using Hl7.Fhir.Utility; using Newtonsoft.Json.Linq; using System; @@ -19,33 +18,33 @@ namespace Firely.Fhir.Validation /// schema to be succesful. /// [DataContract] - internal class ElementSchema : IGroupValidatable + public class ElementSchema : IGroupValidatable { /// /// The unique id for this schema. /// [DataMember] - public Canonical Id { get; private set; } + internal Canonical Id { get; private set; } /// /// The member assertions that constitute this schema. /// [DataMember] - public IReadOnlyCollection Members { get; private set; } + internal IReadOnlyCollection Members { get; private set; } /// /// List of assertions to be performed first before the other statements. Failed assertions in this list will cause the /// other members to fail to execute, which is a performance gain /// - public IReadOnlyCollection ShortcutMembers { get; private set; } + internal IReadOnlyCollection ShortcutMembers { get; private set; } /// /// Lists the present in the members of this schema. /// - public IReadOnlyCollection CardinalityValidators { get; private set; } = Array.Empty(); + internal IReadOnlyCollection CardinalityValidators { get; private set; } = Array.Empty(); /// - public ElementSchema(Canonical id, params IAssertion[] members) : this(id, members.AsEnumerable()) + internal ElementSchema(Canonical id, params IAssertion[] members) : this(id, members.AsEnumerable()) { // nothing } @@ -53,7 +52,7 @@ public ElementSchema(Canonical id, params IAssertion[] members) : this(id, membe /// /// Constructs a new with the given members and id. /// - public ElementSchema(Canonical id, IEnumerable members) + internal ElementSchema(Canonical id, IEnumerable members) { Members = members.ToList(); ShortcutMembers = extractShortcutMembers(Members); @@ -69,9 +68,7 @@ public ElementSchema(Canonical id, IEnumerable members) private static IReadOnlyCollection extractShortcutMembers(IEnumerable members) => members.OfType().ToList(); - - /// - public virtual ResultReport Validate( + internal virtual ResultReport ValidateInternal( IEnumerable input, ValidationContext vc, ValidationState state) @@ -95,11 +92,17 @@ public virtual ResultReport Validate( return ResultReport.Combine(subresult.ToList()); } - /// - public virtual ResultReport Validate(IScopedNode input, ValidationContext vc, ValidationState state) + + /// + ResultReport IGroupValidatable.Validate( + IEnumerable input, + ValidationContext vc, + ValidationState state) => ValidateInternal(input, vc, state); + + internal virtual ResultReport ValidateInternal(IScopedNode input, ValidationContext vc, ValidationState state) { // If we have shortcut members, run them first - if (ShortcutMembers.Any()) + if (ShortcutMembers.Count != 0) { var subResult = ShortcutMembers.Where(vc.Filter).Select(ma => ma.ValidateOne(input, vc, state)); var report = ResultReport.Combine(subResult.ToList()); @@ -111,6 +114,9 @@ public virtual ResultReport Validate(IScopedNode input, ValidationContext vc, Va return ResultReport.Combine(subresult.ToList()); } + /// + ResultReport IValidatable.Validate(IScopedNode input, ValidationContext vc, ValidationState state) => ValidateInternal(input, vc, state); + /// /// Lists additional properties shown as metadata on the schema, separate from the members. /// @@ -155,12 +161,12 @@ static JToken nest(JToken mem) => /// Find the first subschema with the given anchor. /// /// An if found, otherwise null. - public ElementSchema? FindFirstByAnchor(string anchor) => + internal ElementSchema? FindFirstByAnchor(string anchor) => Members.OfType().Select(da => da.FindFirstByAnchor(anchor)).FirstOrDefault(s => s is not null); /// /// Whether the schema has members. /// - public bool IsEmpty() => !Members.Any(); + internal bool IsEmpty() => !Members.Any(); } } diff --git a/src/Firely.Fhir.Validation/Impl/ExtensionSchema.cs b/src/Firely.Fhir.Validation/Impl/ExtensionSchema.cs index fa3174cf..64889fd9 100644 --- a/src/Firely.Fhir.Validation/Impl/ExtensionSchema.cs +++ b/src/Firely.Fhir.Validation/Impl/ExtensionSchema.cs @@ -4,7 +4,6 @@ * via any medium is strictly prohibited. */ -using Hl7.Fhir.ElementModel; using Hl7.Fhir.Support; using System; using System.Collections.Generic; @@ -48,7 +47,7 @@ public ExtensionSchema(StructureDefinitionInformation structureDefinition, IEnum .FirstOrDefault(); // this will actually always be max one, but that's validated by a cardinality validator. /// - public override ResultReport Validate(IEnumerable input, ValidationContext vc, ValidationState state) + internal override ResultReport ValidateInternal(IEnumerable input, ValidationContext vc, ValidationState state) { // Group the instances by their url - this allows a IGroupValidatable schema for the // extension to validate the "extension cardinality". @@ -128,11 +127,11 @@ static ExtensionUrlFollower callback(ExtensionUrlFollower? follower) => /// protected ResultReport ValidateExtensionSchema(IEnumerable input, ValidationContext vc, - ValidationState state) => base.Validate(input, vc, state); + ValidationState state) => base.ValidateInternal(input, vc, state); /// - public override ResultReport Validate(IScopedNode input, ValidationContext vc, ValidationState state) => - Validate(new[] { input }, vc, state); + internal override ResultReport ValidateInternal(IScopedNode input, ValidationContext vc, ValidationState state) => + ValidateInternal(new[] { input }, vc, state); /// diff --git a/src/Firely.Fhir.Validation/Impl/FhirSchema.cs b/src/Firely.Fhir.Validation/Impl/FhirSchema.cs index 18dbffa8..980a8263 100644 --- a/src/Firely.Fhir.Validation/Impl/FhirSchema.cs +++ b/src/Firely.Fhir.Validation/Impl/FhirSchema.cs @@ -4,7 +4,6 @@ * via any medium is strictly prohibited. */ -using Hl7.Fhir.ElementModel; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; @@ -42,19 +41,19 @@ public FhirSchema(StructureDefinitionInformation structureDefinition, IEnumerabl } /// - public override ResultReport Validate(IScopedNode input, ValidationContext vc, ValidationState state) + internal override ResultReport ValidateInternal(IScopedNode input, ValidationContext vc, ValidationState state) { state = state .UpdateLocation(sp => sp.InvokeSchema(this)) .UpdateInstanceLocation(ip => ip.StartResource(input.InstanceType)); - return base.Validate(input, vc, state); + return base.ValidateInternal(input, vc, state); } /// - public override ResultReport Validate(IEnumerable input, ValidationContext vc, ValidationState state) + internal override ResultReport ValidateInternal(IEnumerable input, ValidationContext vc, ValidationState state) { state = state.UpdateLocation(sp => sp.InvokeSchema(this)); - return base.Validate(input, vc, state); + return base.ValidateInternal(input, vc, state); } /// diff --git a/src/Firely.Fhir.Validation/Impl/ResourceSchema.cs b/src/Firely.Fhir.Validation/Impl/ResourceSchema.cs index ebb67a00..6ae084a0 100644 --- a/src/Firely.Fhir.Validation/Impl/ResourceSchema.cs +++ b/src/Firely.Fhir.Validation/Impl/ResourceSchema.cs @@ -4,8 +4,6 @@ * via any medium is strictly prohibited. */ -using Hl7.Fhir.ElementModel; -using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Linq; @@ -56,16 +54,16 @@ static MetaProfileSelector callback(MetaProfileSelector? selector) } /// - public override ResultReport Validate(IEnumerable input, ValidationContext vc, ValidationState state) + internal override ResultReport ValidateInternal(IEnumerable input, ValidationContext vc, ValidationState state) { // Schemas representing the root of a FHIR resource cannot meaningfully be used as a GroupValidatable, // so we'll turn this into a normal IValidatable. - var results = input.Select((i, index) => Validate(i, vc, state.UpdateInstanceLocation(d => d.ToIndex(index)))); + var results = input.Select((i, index) => ValidateInternal(i, vc, state.UpdateInstanceLocation(d => d.ToIndex(index)))); return ResultReport.Combine(results.ToList()); } /// - public override ResultReport Validate(IScopedNode input, ValidationContext vc, ValidationState state) + internal override ResultReport ValidateInternal(IScopedNode input, ValidationContext vc, ValidationState state) { // FHIR specific rule about dealing with abstract datatypes (not profiles!): if this schema is an abstract datatype, // we need to run validation against the schema for the actual type, not the abstract type. @@ -76,7 +74,7 @@ public override ResultReport Validate(IScopedNode input, ValidationContext vc, V var typeProfile = vc.TypeNameMapper.MapTypeName(input.InstanceType); var fetchResult = FhirSchemaGroupAnalyzer.FetchSchema(vc.ElementSchemaResolver, state.UpdateLocation(d => d.InvokeSchema(this)), typeProfile); - return fetchResult.Success ? fetchResult.Schema!.Validate(input, vc, state) : fetchResult.Error!; + return fetchResult.Success ? fetchResult.Schema!.ValidateInternal(input, vc, state) : fetchResult.Error!; } // Update instance location state to start of a new Resource @@ -103,7 +101,7 @@ public override ResultReport Validate(IScopedNode input, ValidationContext vc, V // Now that we have fetched the set of most appropriate profiles, call their constraint validation - // this should exclude the special fetch magic for Meta.profile (this function) to avoid a loop, so we call the actual validation here. var validationResult = minimalSet.Select(s => s.ValidateResourceSchema(input, vc, state)).ToList(); - var validationResultOther = fetchedNonFhirSchemas.Select(s => s.Validate(input, vc, state)).ToList(); + var validationResultOther = fetchedNonFhirSchemas.Select(s => s.ValidateInternal(input, vc, state)).ToList(); return ResultReport.Combine(fetchErrors.Append(consistencyReport).Concat(validationResult).Concat(validationResultOther).ToArray()); } @@ -119,7 +117,7 @@ protected ResultReport ValidateResourceSchema(IScopedNode input, ValidationConte () => { state.Global.ResourcesValidated += 1; - return base.Validate(input, vc, state); + return base.ValidateInternal(input, vc, state); }); } diff --git a/src/Firely.Fhir.Validation/Impl/SchemaReferenceValidator.cs b/src/Firely.Fhir.Validation/Impl/SchemaReferenceValidator.cs index e3120728..c0861e5c 100644 --- a/src/Firely.Fhir.Validation/Impl/SchemaReferenceValidator.cs +++ b/src/Firely.Fhir.Validation/Impl/SchemaReferenceValidator.cs @@ -4,7 +4,6 @@ * via any medium is strictly prohibited. */ -using Hl7.Fhir.ElementModel; using Hl7.Fhir.Model; using Newtonsoft.Json.Linq; using System; @@ -48,7 +47,7 @@ public ResultReport Validate(IEnumerable input, ValidationContext v return FhirSchemaGroupAnalyzer.FetchSchema(vc.ElementSchemaResolver, state, SchemaUri) switch { - (var schema, null, _) => schema!.Validate(input, vc, state), + (var schema, null, _) => schema!.ValidateInternal(input, vc, state), (_, var error, _) => error }; } diff --git a/src/Firely.Fhir.Validation/PublicAPI.Unshipped.txt b/src/Firely.Fhir.Validation/PublicAPI.Unshipped.txt index 7df5e0df..28cdb2d1 100644 --- a/src/Firely.Fhir.Validation/PublicAPI.Unshipped.txt +++ b/src/Firely.Fhir.Validation/PublicAPI.Unshipped.txt @@ -15,12 +15,15 @@ Firely.Fhir.Validation.Canonical.Original.get -> string! Firely.Fhir.Validation.Canonical.ToUri() -> System.Uri! Firely.Fhir.Validation.Canonical.Uri.get -> string? Firely.Fhir.Validation.Canonical.Version.get -> string? +Firely.Fhir.Validation.ElementSchema Firely.Fhir.Validation.ExtensionUrlFollower Firely.Fhir.Validation.ExtensionUrlHandling Firely.Fhir.Validation.ExtensionUrlHandling.DontResolve = 0 -> Firely.Fhir.Validation.ExtensionUrlHandling Firely.Fhir.Validation.ExtensionUrlHandling.ErrorIfMissing = 2 -> Firely.Fhir.Validation.ExtensionUrlHandling Firely.Fhir.Validation.ExtensionUrlHandling.WarnIfMissing = 1 -> Firely.Fhir.Validation.ExtensionUrlHandling Firely.Fhir.Validation.IAssertion +Firely.Fhir.Validation.IElementSchemaResolver +Firely.Fhir.Validation.IElementSchemaResolver.GetSchema(Firely.Fhir.Validation.Canonical! schemaUri) -> Firely.Fhir.Validation.ElementSchema? Firely.Fhir.Validation.IExternalReferenceResolver Firely.Fhir.Validation.IExternalReferenceResolver.ResolveAsync(string! reference) -> System.Threading.Tasks.Task! Firely.Fhir.Validation.IJsonSerializable @@ -116,6 +119,7 @@ virtual Firely.Fhir.Validation.Canonical.$() -> Firely.Fhir.Validation.Ca virtual Firely.Fhir.Validation.Canonical.EqualityContract.get -> System.Type! virtual Firely.Fhir.Validation.Canonical.Equals(Firely.Fhir.Validation.Canonical? other) -> bool virtual Firely.Fhir.Validation.Canonical.PrintMembers(System.Text.StringBuilder! builder) -> bool +virtual Firely.Fhir.Validation.ElementSchema.ToJson() -> Newtonsoft.Json.Linq.JToken! virtual Firely.Fhir.Validation.ExtensionUrlFollower.Invoke(string! location, Firely.Fhir.Validation.Canonical? url) -> Firely.Fhir.Validation.ExtensionUrlHandling virtual Firely.Fhir.Validation.MetaProfileSelector.Invoke(string! location, Firely.Fhir.Validation.Canonical![]! originalProfiles) -> Firely.Fhir.Validation.Canonical![]! virtual Firely.Fhir.Validation.TypeNameMapper.Invoke(string! local) -> Firely.Fhir.Validation.Canonical? diff --git a/src/Firely.Fhir.Validation/Schema/IElementSchemaResolver.cs b/src/Firely.Fhir.Validation/Schema/IElementSchemaResolver.cs index 7790c132..dbf2ad7b 100644 --- a/src/Firely.Fhir.Validation/Schema/IElementSchemaResolver.cs +++ b/src/Firely.Fhir.Validation/Schema/IElementSchemaResolver.cs @@ -9,7 +9,7 @@ namespace Firely.Fhir.Validation /// /// An interface for objects that let you obtain an by its schema uri. /// - internal interface IElementSchemaResolver + public interface IElementSchemaResolver { /// /// Retrieve a schema by its schema uri. diff --git a/test/Firely.Fhir.Validation.Compilation.Tests.Shared/ResourceSchemaValidationTests.cs b/test/Firely.Fhir.Validation.Compilation.Tests.Shared/ResourceSchemaValidationTests.cs index f38aebdf..993be1b6 100644 --- a/test/Firely.Fhir.Validation.Compilation.Tests.Shared/ResourceSchemaValidationTests.cs +++ b/test/Firely.Fhir.Validation.Compilation.Tests.Shared/ResourceSchemaValidationTests.cs @@ -87,7 +87,7 @@ public void AvoidsRedoingProfileValidation() vc.ResolveExternalReference = resolveTestData; var validationState = new ValidationState(); - var result = schemaElement!.Validate(new ScopedNode(all.ToTypedElement()).AsScopedNode(), vc, validationState); + var result = schemaElement!.ValidateInternal(new ScopedNode(all.ToTypedElement()).AsScopedNode(), vc, validationState); result.Result.Should().Be(ValidationResult.Failure); var issues = result.Evidence.OfType().ToList(); issues.Count.Should().Be(1); // Bundle.entry[2].resource[0] is validated twice against different profiles.