diff --git a/firely-validator-api-tests.props b/firely-validator-api-tests.props index 5de794fb..406be249 100644 --- a/firely-validator-api-tests.props +++ b/firely-validator-api-tests.props @@ -8,7 +8,7 @@ - 5.7.0 + 5.8.1 diff --git a/firely-validator-api.props b/firely-validator-api.props index 94046397..35de9701 100644 --- a/firely-validator-api.props +++ b/firely-validator-api.props @@ -27,7 +27,7 @@ - 5.7.0 + 5.8.1 diff --git a/src/Firely.Fhir.Validation.Shared/Validator.cs b/src/Firely.Fhir.Validation.Shared/Validator.cs index 59459a09..090dbc26 100644 --- a/src/Firely.Fhir.Validation.Shared/Validator.cs +++ b/src/Firely.Fhir.Validation.Shared/Validator.cs @@ -82,6 +82,9 @@ public Validator( internal OperationOutcome Validate(IScopedNode sn, string? profile = null) { + if (sn.InstanceType is null) + throw new ArgumentException($"Cannot validate the resource because {nameof(IScopedNode)} does not have an instance type."); + profile ??= Canonical.ForCoreType(sn.InstanceType).ToString(); #pragma warning disable CS0618 // Type or member is obsolete diff --git a/src/Firely.Fhir.Validation/Impl/CanonicalValidator.cs b/src/Firely.Fhir.Validation/Impl/CanonicalValidator.cs index 5c07547a..efbdc009 100644 --- a/src/Firely.Fhir.Validation/Impl/CanonicalValidator.cs +++ b/src/Firely.Fhir.Validation/Impl/CanonicalValidator.cs @@ -45,7 +45,7 @@ ResultReport IValidatable.Validate(IScopedNode input, ValidationSettings vc, Val } default: return new IssueAssertion(Issue.CONTENT_ELEMENT_INVALID_PRIMITIVE_VALUE, - $"Primitive does not have the correct type ({input.Value.GetType()})").AsResult(state); + $"Primitive does not have the correct type ({input.Value?.GetType()})").AsResult(state); } } } diff --git a/src/Firely.Fhir.Validation/Impl/ChildrenValidator.cs b/src/Firely.Fhir.Validation/Impl/ChildrenValidator.cs index 603d0899..d9196731 100644 --- a/src/Firely.Fhir.Validation/Impl/ChildrenValidator.cs +++ b/src/Firely.Fhir.Validation/Impl/ChildrenValidator.cs @@ -9,6 +9,7 @@ using Hl7.Fhir.Support; using Hl7.Fhir.Utility; using Newtonsoft.Json.Linq; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; @@ -87,6 +88,9 @@ public JToken ToJson() => /// ResultReport IValidatable.Validate(IScopedNode input, ValidationSettings vc, ValidationState state) { + if (input.InstanceType is null) + throw new ArgumentException($"Cannot validate the resource because {nameof(IScopedNode)} does not have an instance type."); + var evidence = new List(); // Listing children can be an expensive operation, so make sure we run it once. diff --git a/src/Firely.Fhir.Validation/Impl/DatatypeSchema.cs b/src/Firely.Fhir.Validation/Impl/DatatypeSchema.cs index c38e9b49..c38bdef0 100644 --- a/src/Firely.Fhir.Validation/Impl/DatatypeSchema.cs +++ b/src/Firely.Fhir.Validation/Impl/DatatypeSchema.cs @@ -55,6 +55,11 @@ internal override ResultReport ValidateInternal(IScopedNode input, ValidationSet if (vc.ElementSchemaResolver is null) throw new ArgumentException($"Cannot validate the resource because {nameof(ValidationSettings)} does not contain an ElementSchemaResolver."); + if (input.InstanceType is null) + { + throw new ArgumentException($"Cannot validate the resource because {nameof(IScopedNode)} does not have an instance type."); + } + var typeProfile = vc.TypeNameMapper.MapTypeName(input.InstanceType); var fetchResult = FhirSchemaGroupAnalyzer.FetchSchema(vc.ElementSchemaResolver, state, typeProfile); return fetchResult.Success ? fetchResult.Schema!.ValidateInternal(input, vc, state) : fetchResult.Error!; diff --git a/src/Firely.Fhir.Validation/Impl/FhirSchema.cs b/src/Firely.Fhir.Validation/Impl/FhirSchema.cs index 8f8813f4..9feff8e3 100644 --- a/src/Firely.Fhir.Validation/Impl/FhirSchema.cs +++ b/src/Firely.Fhir.Validation/Impl/FhirSchema.cs @@ -7,6 +7,7 @@ */ using Newtonsoft.Json.Linq; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; @@ -51,6 +52,9 @@ public FhirSchema(StructureDefinitionInformation structureDefinition, IEnumerabl /// internal override ResultReport ValidateInternal(IScopedNode input, ValidationSettings vc, ValidationState state) { + if (input.InstanceType is null) + throw new ArgumentException($"Cannot validate the resource because {nameof(IScopedNode)} does not have an instance type."); + state = state .UpdateLocation(sp => sp.InvokeSchema(this)) .UpdateInstanceLocation(ip => ip.StartResource(input.InstanceType)); diff --git a/src/Firely.Fhir.Validation/Impl/FhirStringValidator.cs b/src/Firely.Fhir.Validation/Impl/FhirStringValidator.cs index 2d817859..81f9c923 100644 --- a/src/Firely.Fhir.Validation/Impl/FhirStringValidator.cs +++ b/src/Firely.Fhir.Validation/Impl/FhirStringValidator.cs @@ -36,7 +36,7 @@ ResultReport IValidatable.Validate(IScopedNode input, ValidationSettings vc, Val } default: return new IssueAssertion(Issue.CONTENT_ELEMENT_INVALID_PRIMITIVE_VALUE, - $"Primitive does not have the correct type ({input.Value.GetType()})").AsResult(state); + $"Primitive does not have the correct type ({input.Value?.GetType()})").AsResult(state); } } } diff --git a/src/Firely.Fhir.Validation/Impl/FhirTxt2Validator.cs b/src/Firely.Fhir.Validation/Impl/FhirTxt2Validator.cs index ff03c5f4..c176abe5 100644 --- a/src/Firely.Fhir.Validation/Impl/FhirTxt2Validator.cs +++ b/src/Firely.Fhir.Validation/Impl/FhirTxt2Validator.cs @@ -41,7 +41,7 @@ public class FhirTxt2Validator : InvariantValidator internal override (bool, ResultReport?) RunInvariant(IScopedNode input, ValidationSettings vc, ValidationState _) { //Check whether the narrative contains non-whitespace content. - return (!string.IsNullOrWhiteSpace(input.Value.ToString()), null); + return (!string.IsNullOrWhiteSpace(input.Value?.ToString()), null); } /// diff --git a/src/Firely.Fhir.Validation/Impl/FixedValidator.cs b/src/Firely.Fhir.Validation/Impl/FixedValidator.cs index 05085f37..96d0d88f 100644 --- a/src/Firely.Fhir.Validation/Impl/FixedValidator.cs +++ b/src/Firely.Fhir.Validation/Impl/FixedValidator.cs @@ -56,7 +56,7 @@ ResultReport IValidatable.Validate(IScopedNode input, ValidationSettings _, Vali return ResultReport.SUCCESS; static string displayValue(ITypedElement te) => - te.Children().Any() ? te.ToJson() : te.Value.ToString()!; + te.Children().Any() ? te.ToJson() : te.Value!.ToString()!; } /// diff --git a/src/Firely.Fhir.Validation/Impl/PatternValidator.cs b/src/Firely.Fhir.Validation/Impl/PatternValidator.cs index af1e3e8f..87ec9a96 100644 --- a/src/Firely.Fhir.Validation/Impl/PatternValidator.cs +++ b/src/Firely.Fhir.Validation/Impl/PatternValidator.cs @@ -58,7 +58,7 @@ ResultReport IValidatable.Validate(IScopedNode input, ValidationSettings _, Vali return result; static string displayValue(ITypedElement te) => - te.Children().Any() ? te.ToJson() : te.Value.ToString()!; + te.Children().Any() ? te.ToJson() : te.Value!.ToString()!; } /// diff --git a/src/Firely.Fhir.Validation/Impl/ReferencedInstanceValidator.cs b/src/Firely.Fhir.Validation/Impl/ReferencedInstanceValidator.cs index bce90b96..4304bc45 100644 --- a/src/Firely.Fhir.Validation/Impl/ReferencedInstanceValidator.cs +++ b/src/Firely.Fhir.Validation/Impl/ReferencedInstanceValidator.cs @@ -73,6 +73,9 @@ ResultReport IValidatable.Validate(IScopedNode input, ValidationSettings vc, Val if (vc.ElementSchemaResolver is null) throw new ArgumentException($"Cannot validate because {nameof(ValidationSettings)} does not contain an ElementSchemaResolver."); + if (input.InstanceType is null) + throw new ArgumentException($"Cannot validate the resource because {nameof(IScopedNode)} does not have an instance type."); + if (!IsSupportedReferenceType(input.InstanceType)) return new IssueAssertion(Issue.CONTENT_REFERENCE_OF_INVALID_KIND, $"Expected a reference type here (reference or canonical) not a {input.InstanceType}.") @@ -127,7 +130,7 @@ private record ResolutionResult(ITypedElement? ReferencedResource, AggregationMo // First, try to resolve within this instance (in contained, Bundle.entry) resolveLocally(input.ToScopedNode(), reference, s, out var resolution) ]; - + // Now that we have tried to fetch the reference locally, we have also determined the kind of // reference we are dealing with, so check it for aggregation and versioning rules. if (HasAggregation && AggregationRules?.Any(a => a == resolution.ReferenceKind) == false) @@ -258,9 +261,9 @@ public JToken ToJson() /// /// Whether this validator supports validating a given reference type. /// - internal static bool IsSupportedReferenceType(string typeCode) => + internal static bool IsSupportedReferenceType(string typeCode) => IsReferenceType(typeCode) && typeCode is not "canonical"; - + /// /// Whether a type is a reference type. /// diff --git a/src/Firely.Fhir.Validation/Impl/ResourceSchema.cs b/src/Firely.Fhir.Validation/Impl/ResourceSchema.cs index 7a4964d1..c695210b 100644 --- a/src/Firely.Fhir.Validation/Impl/ResourceSchema.cs +++ b/src/Firely.Fhir.Validation/Impl/ResourceSchema.cs @@ -74,6 +74,9 @@ internal override ResultReport ValidateInternal(IEnumerable input, /// internal override ResultReport ValidateInternal(IScopedNode input, ValidationSettings vc, ValidationState state) { + if (input.InstanceType is null) + throw new ArgumentException($"Cannot validate the resource because {nameof(IScopedNode)} does not have an instance type."); + // 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. if (StructureDefinition.IsAbstract && StructureDefinition.Derivation != StructureDefinitionInformation.TypeDerivationRule.Constraint) diff --git a/src/Firely.Fhir.Validation/Schema/ValueElementNode.cs b/src/Firely.Fhir.Validation/Schema/ValueElementNode.cs index 2a49c1e5..e0c2428a 100644 --- a/src/Firely.Fhir.Validation/Schema/ValueElementNode.cs +++ b/src/Firely.Fhir.Validation/Schema/ValueElementNode.cs @@ -23,9 +23,9 @@ public ValueElementNode(IScopedNode wrapped) public string Name => "value"; - public string InstanceType => TypeSpecifier.ForNativeType(_wrapped.Value.GetType()).FullName; + public string? InstanceType => (_wrapped.Value is not null) ? TypeSpecifier.ForNativeType(_wrapped.Value.GetType()).FullName : null; - public object Value => _wrapped.Value; + public object? Value => _wrapped.Value; public IEnumerable Children(string? name = null) => Enumerable.Empty(); } diff --git a/src/Firely.Fhir.Validation/Support/ScopedNodeToTypedElementAdapter.cs b/src/Firely.Fhir.Validation/Support/ScopedNodeToTypedElementAdapter.cs index a33d9314..8c80f4a1 100644 --- a/src/Firely.Fhir.Validation/Support/ScopedNodeToTypedElementAdapter.cs +++ b/src/Firely.Fhir.Validation/Support/ScopedNodeToTypedElementAdapter.cs @@ -36,9 +36,9 @@ public ScopedNodeToTypedElementAdapter(IScopedNode adaptee) public string Name => _adaptee.Name; - public string InstanceType => _adaptee.InstanceType; + public string? InstanceType => _adaptee.InstanceType; - public object Value => _adaptee.Value; + public object? Value => _adaptee.Value; public IEnumerable Children(string? name = null) => _adaptee.Children(name).Select(n => new ScopedNodeToTypedElementAdapter(n)); diff --git a/src/Firely.Fhir.Validation/Support/TypedElementToIScopedNodeToAdapter.cs b/src/Firely.Fhir.Validation/Support/TypedElementToIScopedNodeToAdapter.cs index 28294ab8..40d6e207 100644 --- a/src/Firely.Fhir.Validation/Support/TypedElementToIScopedNodeToAdapter.cs +++ b/src/Firely.Fhir.Validation/Support/TypedElementToIScopedNodeToAdapter.cs @@ -29,9 +29,9 @@ private TypedElementToIScopedNodeToAdapter(ITypedElement adaptee) public string Name => _adaptee.Name; - public string InstanceType => _adaptee.InstanceType; + public string? InstanceType => _adaptee.InstanceType; - public object Value => _adaptee.Value; + public object? Value => _adaptee.Value; IEnumerable IBaseElementNavigator.Children(string? name) => _adaptee.Children(name).Select(n => new TypedElementToIScopedNodeToAdapter(n)); diff --git a/test/Firely.Fhir.Validation.Compilation.Tests.Shared/FhirTests/DotNetValidator.cs b/test/Firely.Fhir.Validation.Compilation.Tests.Shared/FhirTests/DotNetValidator.cs index f6f3b404..86a7c814 100644 --- a/test/Firely.Fhir.Validation.Compilation.Tests.Shared/FhirTests/DotNetValidator.cs +++ b/test/Firely.Fhir.Validation.Compilation.Tests.Shared/FhirTests/DotNetValidator.cs @@ -137,7 +137,7 @@ IEnumerable getProfiles(ITypedElement node, string? profile = null) yield return profile; } - var instanceType = ModelInfo.CanonicalUriForFhirCoreType(node.InstanceType); + var instanceType = node.InstanceType is not null ? ModelInfo.CanonicalUriForFhirCoreType(node.InstanceType) : null; if (instanceType is not null) { yield return instanceType!; diff --git a/test/Firely.Fhir.Validation.Tests/Support/ElementNodeAdapter.cs b/test/Firely.Fhir.Validation.Tests/Support/ElementNodeAdapter.cs index a0d7f8bc..134cd216 100644 --- a/test/Firely.Fhir.Validation.Tests/Support/ElementNodeAdapter.cs +++ b/test/Firely.Fhir.Validation.Tests/Support/ElementNodeAdapter.cs @@ -20,13 +20,13 @@ internal class ElementNodeAdapter : ITypedElement public string Name => _elementNodeInstance.Name; - public string InstanceType => _elementNodeInstance.InstanceType; + public string InstanceType => _elementNodeInstance.InstanceType!; - public object Value => _elementNodeInstance.Value; + public object Value => _elementNodeInstance.Value!; public string Location => _elementNodeInstance.Location; - public IElementDefinitionSummary Definition => _elementNodeInstance.Definition; + public IElementDefinitionSummary Definition => _elementNodeInstance.Definition!; public static ElementNodeAdapter Root(string type, string? name = null, object? value = null) { diff --git a/test/Firely.Fhir.Validation.Tests/Support/TypedElementOnDictionary.cs b/test/Firely.Fhir.Validation.Tests/Support/TypedElementOnDictionary.cs index 2b9421bd..2075ab36 100644 --- a/test/Firely.Fhir.Validation.Tests/Support/TypedElementOnDictionary.cs +++ b/test/Firely.Fhir.Validation.Tests/Support/TypedElementOnDictionary.cs @@ -45,7 +45,7 @@ private static ITypedElement forObject(string name, object value, string locatio else { _ = ElementNode.TryConvertToElementValue(value, out var primitive); - return new ConstantElement(name, ts.FullName, primitive, location); + return new ConstantElement(name, ts.FullName, primitive!, location); } } } @@ -102,7 +102,7 @@ public IEnumerable Children(string? name) => Enumerable.Empty(); } - public IEnumerable? Annotations(Type type) => type == typeof(IResourceTypeSupplier) ? (new[] { this }) : null; + public IEnumerable Annotations(Type type) => type == typeof(IResourceTypeSupplier) ? (new[] { this }) : Enumerable.Empty(); public string? ResourceType => TryGetValue("resourceType", out var rt) ? rt as string : null;