diff --git a/src/Firely.Fhir.Validation.Compilation.Shared/SchemaBuilders/TypeReferenceBuilder.cs b/src/Firely.Fhir.Validation.Compilation.Shared/SchemaBuilders/TypeReferenceBuilder.cs index 6eaecb06..5c1d57f3 100644 --- a/src/Firely.Fhir.Validation.Compilation.Shared/SchemaBuilders/TypeReferenceBuilder.cs +++ b/src/Firely.Fhir.Validation.Compilation.Shared/SchemaBuilders/TypeReferenceBuilder.cs @@ -191,13 +191,12 @@ private static bool shouldValidateTypeReference(ElementDefinitionNavigator nav) ? new AllValidator(profileAssertions, validateReferenceAssertion) : validateReferenceAssertion; } - else if (!ReferencedInstanceValidator.IsReferenceType(code) && typeRef.TargetProfile.Any()) - { - throw new IncorrectElementDefinitionException($"Encountered targetProfiles {string.Join(",", typeRef.TargetProfile)} on an element that is not " + - $"a reference type (canonical or Reference) but a {code}."); - } else + { + // A targetProfile on an element that is not a reference type (canonical or Reference) is + // meaningless, but not a reason to stop compiling the whole profile: ignore it. return profileAssertions; + } } diff --git a/test/Firely.Fhir.Validation.Compilation.Tests.R4/TargetProfileOnNonReferenceTests.cs b/test/Firely.Fhir.Validation.Compilation.Tests.R4/TargetProfileOnNonReferenceTests.cs new file mode 100644 index 00000000..f0e53547 --- /dev/null +++ b/test/Firely.Fhir.Validation.Compilation.Tests.R4/TargetProfileOnNonReferenceTests.cs @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2024, Firely (info@fire.ly) and contributors + * See the file CONTRIBUTORS for details. + * + * This file is licensed under the BSD 3-Clause license + * available at https://github.com/FirelyTeam/firely-validator-api/blob/main/LICENSE + */ + +using FluentAssertions; +using Hl7.Fhir.Model; +using Hl7.Fhir.Specification.Source; +using Hl7.Fhir.Specification.Terminology; +using System.Linq; +using Xunit; + +namespace Firely.Fhir.Validation.Tests +{ + public class TargetProfileOnNonReferenceTests + { + private const string PROFILE_URL = "http://example.org/fhir/StructureDefinition/TargetProfileOnNonReferenceTestProfile"; + + private static StructureDefinition profile(string type, params ElementDefinition[] differential) => new() + { + Url = PROFILE_URL, + Name = "TargetProfileOnNonReferenceTestProfile", + Status = PublicationStatus.Draft, + FhirVersion = FHIRVersion.N4_0_1, + Kind = StructureDefinition.StructureDefinitionKind.Resource, + Abstract = false, + Type = type, + BaseDefinition = $"http://hl7.org/fhir/StructureDefinition/{type}", + Derivation = StructureDefinition.TypeDerivationRule.Constraint, + Differential = new StructureDefinition.DifferentialComponent { Element = differential.ToList() } + }; + + private static OperationOutcome validate(StructureDefinition sd, Resource instance) + { + var source = new SnapshotSource(new MultiResolver(new InMemoryProfileResolver(sd), ZipSource.CreateValidationSource())); + var validator = new Validator(source.AsAsync(), new LocalTerminologyService(source.AsAsync())); + return validator.Validate(instance, PROFILE_URL); + } + + /// + /// A targetProfile on an element that is not a reference is meaningless, but should not + /// prevent the profile from being compiled. See issue #423. + /// + [Fact] + public void TargetProfileOnNonReferenceTypeShouldBeIgnored() + { + var sd = profile("Patient", + new ElementDefinition("Patient.birthDate") + { + ElementId = "Patient.birthDate", + Type = + [ + new ElementDefinition.TypeRefComponent + { + Code = "date", + TargetProfile = ["http://hl7.org/fhir/StructureDefinition/Patient"] + } + ] + }); + + var patient = new Patient { BirthDate = "1974-12-25" }; + + var outcome = validate(sd, patient); + + outcome.Success.Should().BeTrue(outcome.ToString()); + } + } +}