Exemple #1
0
        private static OperationOutcome ValidateMatch(this Validator validator, Match match, IElementNavigator parent)
        {
            var outcome = new OperationOutcome();

            var definition = match.Definition.Current;

            if (definition.Min == null)
            {
                validator.Trace(outcome, $"Element definition does not specify a 'min' value, which is required. Cardinality has not been validated",
                                Issue.PROFILE_ELEMENTDEF_CARDINALITY_MISSING, parent);
            }
            else if (definition.Max == null)
            {
                validator.Trace(outcome, $"Element definition does not specify a 'max' value, which is required. Cardinality has not been validated",
                                Issue.PROFILE_ELEMENTDEF_CARDINALITY_MISSING, parent);
            }

            var cardinality = Cardinality.FromElementDefinition(definition);

            var bucket = BucketFactory.CreateRoot(match.Definition, validator);

            foreach (var element in match.InstanceElements)
            {
                var success = bucket.Add(element);

                // For the "root" slice group (=the original core element that was sliced, not resliced)
                // any element that does not match is an error
                // Since the ChildNameMatcher currently does the matching, this will never go wrong
            }

            outcome.Add(bucket.Validate(validator, parent));

            return(outcome);
        }
        private static CompiledExpression getExecutableConstraint(Validator v, OperationOutcome outcome, IElementNavigator instance,
                                                                  ElementDefinition.ConstraintComponent constraintElement)
        {
            var compiledExpression = constraintElement.Annotation <CompiledConstraintAnnotation>()?.Expression;

            if (compiledExpression == null)
            {
                var fpExpressionText = constraintElement.GetFhirPathConstraint();

                if (fpExpressionText != null)
                {
                    try
                    {
                        compiledExpression = v.FpCompiler.Compile(fpExpressionText);
                        constraintElement.AddAnnotation(new CompiledConstraintAnnotation {
                            Expression = compiledExpression
                        });
                    }
                    catch (Exception e)
                    {
                        v.Trace(outcome, $"Compilation of FhirPath for constraint '{constraintElement.Key}' failed: {e.Message}",
                                Issue.PROFILE_ELEMENTDEF_INVALID_FHIRPATH_EXPRESSION, instance);
                    }
                }
                else
                {
                    v.Trace(outcome, $"Encountered an invariant ({constraintElement.Key}) that has no FhirPath expression, skipping validation of this constraint",
                            Issue.UNSUPPORTED_CONSTRAINT_WITHOUT_FHIRPATH, instance);
                }
            }

            return(compiledExpression);
        }
        internal static OperationOutcome ValidateChildConstraints(this Validator validator, ElementDefinitionNavigator definition,
                                                                  ScopedNode instance, bool allowAdditionalChildren)
        {
            var outcome = new OperationOutcome();

            if (!definition.HasChildren)
            {
                return(outcome);
            }

            validator.Trace(outcome, "Start validation of inlined child constraints for '{0}'".FormatWith(definition.Path), Issue.PROCESSING_PROGRESS, instance);

            var matchResult = ChildNameMatcher.Match(definition, instance);

            if (matchResult.UnmatchedInstanceElements.Any() && !allowAdditionalChildren)
            {
                var elementList = String.Join(",", matchResult.UnmatchedInstanceElements.Select(e => "'" + e.Name + "'"));
                validator.Trace(outcome, $"Encountered unknown child elements {elementList} for definition '{definition.Path}'",
                                Issue.CONTENT_ELEMENT_HAS_UNKNOWN_CHILDREN, instance);
            }

            //TODO: Give warnings for out-of order children.  Really? That's an xml artifact, no such thing in Json!
            //(with the serializationrepresentationnav we could determine the source is xml and make order matter....)

            // Recursively validate my children
            foreach (var match in matchResult.Matches)
            {
                outcome.Add(validator.ValidateMatch(match, instance));
            }

            return(outcome);
        }
        private static IElementNavigator resolveReference(this Validator validator, ScopedNavigator instance, string reference, out ElementDefinition.AggregationMode? referenceKind, OperationOutcome outcome)
        {
            var identity = new ResourceIdentity(reference);

            if (identity.Form == ResourceIdentityForm.Undetermined)
            {
                if (!Uri.IsWellFormedUriString(reference, UriKind.RelativeOrAbsolute))
                {
                    validator.Trace(outcome, $"Encountered an unparseable reference ({reference})", Issue.CONTENT_UNPARSEABLE_REFERENCE, instance);
                    referenceKind = null;
                    return null;
                }
            }

            var result = instance.Resolve(reference);

            if (identity.Form == ResourceIdentityForm.Local)
            {
                referenceKind = ElementDefinition.AggregationMode.Contained;
                if(result == null)
                    validator.Trace(outcome, $"Contained reference ({reference}) is not resolvable", Issue.CONTENT_CONTAINED_REFERENCE_NOT_RESOLVABLE, instance);
            }
            else
            {
                if (result != null)
                    referenceKind = ElementDefinition.AggregationMode.Bundled;
                else
                    referenceKind = ElementDefinition.AggregationMode.Referenced;
            }

            return result;
        }
        public static OperationOutcome ValidateFp(this Validator v, ElementDefinition definition, ScopedNavigator instance)
        {
            var outcome = new OperationOutcome();

            if (!definition.Constraint.Any())
            {
                return(outcome);
            }
            if (v.Settings.SkipConstraintValidation)
            {
                return(outcome);
            }

            var context = instance.AtResource ? instance : instance.Parent;

            foreach (var constraintElement in definition.Constraint)
            {
                bool success = false;

                try
                {
                    var compiled = getExecutableConstraint(v, outcome, instance, constraintElement);
                    success = compiled.Predicate(instance, new FhirEvaluationContext(context)
                    {
                        Resolver = callExternalResolver
                    });
                }
                catch (Exception e)
                {
                    v.Trace(outcome, $"Evaluation of FhirPath for constraint '{constraintElement.Key}' failed: {e.Message}",
                            Issue.PROFILE_ELEMENTDEF_INVALID_FHIRPATH_EXPRESSION, instance);
                }

                if (!success)
                {
                    var text  = "Instance failed constraint " + constraintElement.ConstraintDescription();
                    var issue = constraintElement.Severity == ElementDefinition.ConstraintSeverity.Error ?
                                Issue.CONTENT_ELEMENT_FAILS_ERROR_CONSTRAINT : Issue.CONTENT_ELEMENT_FAILS_WARNING_CONSTRAINT;

                    v.Trace(outcome, text, issue, instance);
                }
            }

            return(outcome);

            IElementNavigator callExternalResolver(string url)
            {
                OperationOutcome o = new OperationOutcome();
                var result         = v.ExternalReferenceResolutionNeeded(url, o, "dummy");

                if (o.Success && result != null)
                {
                    return(result);
                }

                return(null);
            }
        }
        internal static OperationOutcome ValidateType(this Validator validator, ElementDefinition definition, ScopedNavigator instance)
        {
            var outcome = new OperationOutcome();

            validator.Trace(outcome, "Validating against constraints specified by the element's defined type", Issue.PROCESSING_PROGRESS, instance);

            if(definition.Type.Any(tr => tr.Code == null))
                validator.Trace(outcome, "ElementDefinition contains a type with an empty type code", Issue.PROFILE_ELEMENTDEF_CONTAINS_NULL_TYPE, instance);

            // Check if this is a choice: there are multiple distinct Codes to choose from
            var typeRefs = definition.Type.Where(tr => tr.Code != null);
            var choices = typeRefs.Select(tr => tr.Code.Value).Distinct();

            if (choices.Count() > 1)
            {
                if (instance.Type != null)
                {
                    // This is a choice type, find out what type is present in the instance data
                    // (e.g. deceased[Boolean], or _resourceType in json). This is exposed by IElementNavigator.TypeName.
                    var instanceType = ModelInfo.FhirTypeNameToFhirType(instance.Type);
                    if (instanceType != null)
                    {
                        // In fact, the next statements are just an optimalization, without them, we would do an ANY validation
                        // against *all* choices, what we do here is pre-filtering for sensible choices, and report if there isn't
                        // any.
                        var applicableChoices = typeRefs.Where(tr => ModelInfo.IsInstanceTypeFor(tr.Code.Value, instanceType.Value));

                        // Instance typename must be one of the applicable types in the choice
                        if (applicableChoices.Any())
                        {
                            outcome.Include(validator.ValidateTypeReferences(applicableChoices, instance));
                        }
                        else
                        {
                            var choiceList = String.Join(",", choices.Select(t => "'" + t.GetLiteral() + "'"));
                            validator.Trace(outcome, $"Type specified in the instance ('{instance.Type}') is not one of the allowed choices ({choiceList})",
                                     Issue.CONTENT_ELEMENT_HAS_INCORRECT_TYPE, instance);
                        }
                    }
                    else
                        validator.Trace(outcome, $"Instance indicates the element is of type '{instance.Type}', which is not a known FHIR core type.",
                                Issue.CONTENT_ELEMENT_CHOICE_INVALID_INSTANCE_TYPE, instance);
                }
                else
                    validator.Trace(outcome, "ElementDefinition is a choice or contains a polymorphic type constraint, but the instance does not indicate its actual type",
                        Issue.CONTENT_ELEMENT_CANNOT_DETERMINE_TYPE, instance);
            }
            else if (choices.Count() == 1)
            {
                // Only one type present in list of typerefs, all of the typerefs are candidates
                outcome.Include(validator.ValidateTypeReferences(typeRefs, instance));
            }

            return outcome;
        }
        private static OperationOutcome ValidateMatch(this Validator validator, Match match, ScopedNode parent)
        {
            var outcome = new OperationOutcome();

            var definition = match.Definition.Current;

            if (definition.Min == null)
            {
                validator.Trace(outcome, $"Element definition does not specify a 'min' value, which is required. Cardinality has not been validated",
                                Issue.PROFILE_ELEMENTDEF_CARDINALITY_MISSING, parent);
            }
            else if (definition.Max == null)
            {
                validator.Trace(outcome, $"Element definition does not specify a 'max' value, which is required. Cardinality has not been validated",
                                Issue.PROFILE_ELEMENTDEF_CARDINALITY_MISSING, parent);
            }

            var cardinality = Cardinality.FromElementDefinition(definition);

            IBucket bucket;

            try
            {
                bucket = BucketFactory.CreateRoot(match.Definition, validator);
            }
            catch (NotImplementedException ni)
            {
                // Will throw if a non-supported slice type is encountered
                validator.Trace(outcome, ni.Message, Issue.UNSUPPORTED_SLICING_NOT_SUPPORTED, parent);
                return(outcome);
            }

            foreach (var element in match.InstanceElements)
            {
                var success = bucket.Add(element);

                // For the "root" slice group (=the original core element that was sliced, not resliced)
                // any element that does not match is an error
                // Since the ChildNameMatcher currently does the matching, this will never go wrong
            }

            outcome.Add(bucket.Validate(validator, parent));

            return(outcome);
        }
Exemple #8
0
        public virtual OperationOutcome Validate(Validator validator, IElementNavigator errorLocation)
        {
            var outcome = new OperationOutcome();

            if (!Cardinality.InRange(Members.Count))
            {
                validator.Trace(outcome, $"Instance count for '{Name}' is {Members.Count}, which is not within the specified cardinality of {Cardinality.ToString()}",
                                Issue.CONTENT_INCORRECT_OCCURRENCE, errorLocation);
            }

            return(outcome);
        }
Exemple #9
0
        public virtual OperationOutcome Validate(Validator validator, IElementNavigator errorLocation)
        {
            var outcome = new OperationOutcome();

            if (!Cardinality.InRange(Members.Count))
            {
                OperationOutcome.IssueComponent issue = validator.Trace(outcome, $"Instance count for '{Name}' is {Members.Count}, which is not within the specified cardinality of {Cardinality.ToString()}",
                                                                        Issue.CONTENT_INCORRECT_OCCURRENCE, errorLocation);
                if (issue != null)
                {
                    // the location in the structure definition (this will match to the discriminator when checking slicing)
                    // issue.LocationElement.Add(new FhirString(Path));
                    issue.SetAnnotation(new SlicePathAnnotation(Path));
                }
            }
            return(outcome);
        }
        public static OperationOutcome ValidateFixed(this Validator v, ElementDefinition definition, IElementNavigator instance)
        {
            var outcome = new OperationOutcome();

            if (definition.Fixed != null)
            {
                IElementNavigator fixedValueNav = new PocoNavigator(definition.Fixed);

                if (!instance.IsExactlyEqualTo(fixedValueNav))
                {
                    v.Trace(outcome, $"Value is not exactly equal to fixed value '{toReadable(definition.Fixed)}'",
                            Issue.CONTENT_DOES_NOT_MATCH_FIXED_VALUE, instance);
                }
            }

            return(outcome);
        }
        public static OperationOutcome ValidatePattern(this Validator v, ElementDefinition definition, IElementNavigator instance)
        {
            var outcome = new OperationOutcome();

            if (definition.Pattern != null)
            {
                IElementNavigator patternValueNav = new PocoNavigator(definition.Pattern);

                if (!instance.Matches(patternValueNav))
                {
                    v.Trace(outcome, $"Value does not match pattern '{toReadable(definition.Pattern)}'",
                            Issue.CONTENT_DOES_NOT_MATCH_PATTERN_VALUE, instance);
                }
            }

            return(outcome);
        }
        private static OperationOutcome validateMinMaxValue(Validator me, Element definition, ITypedElement instance,
                                                            int comparisonOutcome, string elementName)
        {
            var outcome = new OperationOutcome();

            if (definition != null)
            {
                // Min/max are only defined for ordered types
                if (definition.GetType().IsOrderedFhirType())
                {
                    try
                    {
                        var instanceValue = instance.GetComparableValue(definition.GetType());

                        if (instanceValue != null)
                        {
                            if (Compare(instanceValue, definition) == comparisonOutcome)
                            {
                                var label = comparisonOutcome == -1 ? "smaller than" :
                                            comparisonOutcome == 0 ? "equal to" :
                                            "larger than";
                                var issue = comparisonOutcome == -1 ? Issue.CONTENT_ELEMENT_PRIMITIVE_VALUE_TOO_SMALL :
                                            Issue.CONTENT_ELEMENT_PRIMITIVE_VALUE_TOO_LARGE;

                                outcome.AddIssue($"Instance value '{instanceValue}' is {label} {elementName} '{definition}'", issue, instance);
                            }
                        }
                    }
                    catch (NotSupportedException ns)
                    {
                        outcome.AddIssue($"Comparing the instance against the {elementName} failed: {ns.Message}", Issue.UNSUPPORTED_MIN_MAX_QUANTITY, instance);
                    }
                }
                else
                {
                    me.Trace(outcome, $"{elementName} was given in ElementDefinition, but type '{definition.TypeName}' is not an ordered type", Issue.PROFILE_ELEMENTDEF_MIN_MAX_USES_UNORDERED_TYPE, instance);
                }
            }

            return(outcome);
        }
        public static OperationOutcome Combine(this Validator validator, BatchValidationMode mode, ITypedElement instance, IEnumerable <Func <OperationOutcome> > validations)
        {
            if (validations.Count() == 0)
            {
                return(new OperationOutcome());
            }

            if (validations.Count() == 1)
            {
                // To not pollute the output if there's just a single input, just add it to the output
                return(validations.First()());
            }

            OperationOutcome combinedResult = new OperationOutcome();

            var modeLabel = mode == BatchValidationMode.All ? "ALL" : "ANY";

            validator.Trace(combinedResult, $"Combination of {validations.Count()} child validation runs, {modeLabel} must succeed", Issue.PROCESSING_PROGRESS, instance);

            int failures  = 0;
            int successes = 0;

            List <OperationOutcome> results = new List <OperationOutcome>();

            // Run the given validations one by one, short circuiting when ANY success is enough
            foreach (var validation in validations)
            {
                var result = validation();
                results.Add(result);

                if (result.Success)
                {
                    successes += 1;
                }
                else
                {
                    failures += 1;
                }

                if (mode == BatchValidationMode.Any && successes > 0)
                {
                    break;                                                         // shortcut evaluation
                }
            }

            // Did we have success overall?
            bool success = mode == BatchValidationMode.Any && successes > 0 ||
                           mode == BatchValidationMode.All && failures == 0 ||
                           mode == BatchValidationMode.Once && successes == 1;


            // Now, build final report

            for (var index = 0; index < results.Count; index++)
            {
                var result = results[index];
                validator.Trace(combinedResult, $"Report {index}: {(result.Success ? "SUCCESS" : "FAILURE")}", Issue.PROCESSING_PROGRESS, instance);

                if (success)
                {
                    // We'd like to include all results of the combined reports, but if the total result is a success,
                    // any errors in failing runs should just be informational
                    if (!result.Success)
                    {
                        result.MakeInformational();
                    }
                }

                combinedResult.Include(result);
            }

            if (success)
            {
                validator.Trace(combinedResult, "Combined validation succeeded", Issue.PROCESSING_PROGRESS, instance);
            }
            else
            {
                combinedResult.AddIssue($"Combined {modeLabel} validation failed, {failures} child validation runs failed, {successes} succeeded", Issue.PROCESSING_PROGRESS, instance);
            }


            return(combinedResult);
        }
        internal static OperationOutcome ValidateResourceReference(this Validator validator, IElementNavigator instance, ElementDefinition.TypeRefComponent typeRef)
        {
            var outcome = new OperationOutcome();

            var references = instance.GetChildrenByName("reference");
            var reference  = references.FirstOrDefault()?.Value as string;

            if (reference == null)       // No reference found -> this is always valid
            {
                return(outcome);
            }

            if (references.Count() > 1)
            {
                validator.Trace(outcome, $"Encountered multiple references, just using first ({reference})", Issue.CONTENT_REFERENCE_HAS_MULTIPLE_REFERENCES, instance);
            }

            // Try to resolve the reference *within* the current instance (Bundle, resource with contained resources) first
            ElementDefinition.AggregationMode?encounteredKind;
            var referencedResource = validator.ResolveReference(instance, reference, out encounteredKind, outcome);

            // Validate the kind of aggregation.
            // If no aggregation is given, all kinds of aggregation are allowed, otherwise only allow
            // those aggregation types that are given in the Aggregation element
            bool hasAggregation = typeRef.Aggregation != null && typeRef.Aggregation.Count() != 0;

            if (hasAggregation && !typeRef.Aggregation.Any(a => a == encounteredKind))
            {
                validator.Trace(outcome, $"Encountered a reference ({reference}) of kind '{encounteredKind}' which is not allowed", Issue.CONTENT_REFERENCE_OF_INVALID_KIND, instance);
            }

            // If we failed to find a referenced resource within the current instance, try to resolve it using an external method
            if (referencedResource == null && encounteredKind == ElementDefinition.AggregationMode.Referenced)
            {
                try
                {
                    referencedResource = validator.ExternalReferenceResolutionNeeded(reference, outcome, instance);
                }
                catch (Exception e)
                {
                    validator.Trace(outcome, $"Resolution of external reference {reference} failed. Message: {e.Message}",
                                    Issue.UNAVAILABLE_REFERENCED_RESOURCE, instance);
                }
            }

            // If the reference was resolved (either internally or externally, validate it
            if (referencedResource != null)
            {
                validator.Trace(outcome, $"Starting validation of referenced resource {reference} ({encounteredKind})", Issue.PROCESSING_START_NESTED_VALIDATION, instance);

                // References within the instance are dealt with within the same validator,
                // references to external entities will operate within a new instance of a validator (and hence a new tracking context).
                // In both cases, the outcome is included in the result.
                if (encounteredKind != ElementDefinition.AggregationMode.Referenced)
                {
                    outcome.Include(validator.Validate(referencedResource, typeRef.GetDeclaredProfiles(), statedProfiles: null, statedCanonicals: null));
                }
                else
                {
                    var newValidator = validator.NewInstance();
                    outcome.Include(newValidator.Validate(referencedResource, typeRef.GetDeclaredProfiles(), statedProfiles: null, statedCanonicals: null));
                }
            }
            else
            {
                validator.Trace(outcome, $"Cannot resolve reference {reference}", Issue.UNAVAILABLE_REFERENCED_RESOURCE, instance);
            }

            return(outcome);
        }
        public OperationOutcome Validate(Validator validator, IElementNavigator errorLocation)
        {
            var outcome = Entry.Validate(validator, errorLocation);   // Validate against entry slice, e.g. cardinality

            var lastMatchingSlice = -1;
            var openTailInUse     = false;

            // Go over the elements in the instance, in order
            foreach (var candidate in Entry.Members)
            {
                bool hasSucceeded = false;

                // Try to find the child slice that this element matches
                for (var sliceNumber = 0; sliceNumber < ChildSlices.Count; sliceNumber++)
                {
                    var sliceName = ChildSlices[sliceNumber].Name;
                    var success   = ChildSlices[sliceNumber].Add(candidate);

                    if (success)
                    {
                        // The instance matched a slice that we have already passed, if order matters,
                        // this is not allowed
                        if (sliceNumber < lastMatchingSlice && Ordered)
                        {
                            validator.Trace(outcome, $"Element matches slice '{sliceName}', but this " +
                                            $"is out of order for group '{Name}', since a previous element already matched slice '{ChildSlices[lastMatchingSlice].Name}'",
                                            Issue.CONTENT_ELEMENT_SLICING_OUT_OF_ORDER, candidate);
                        }
                        else
                        {
                            lastMatchingSlice = sliceNumber;
                        }

                        if (openTailInUse)
                        {
                            // We found a match while we already added a non-match to a "open at end" slicegroup, that's not allowed
                            validator.Trace(outcome, $"Element matched slice '{sliceName}', but it appears after a non-match, which is not allowed for an open-at-end group",
                                            Issue.CONTENT_ELEMENT_FAILS_SLICING_RULE, candidate);
                        }

                        hasSucceeded = true;
                    }
                }

                // So we found no slice that can take this candidate, let's take a look at the rules
                if (!hasSucceeded)
                {
                    if (Rules == ElementDefinition.SlicingRules.Open)
                    {
                        validator.Trace(outcome, $"Element was determined to be in the open slice for group '{Name}'", Issue.PROCESSING_PROGRESS, candidate);
                    }
                    else if (Rules == ElementDefinition.SlicingRules.OpenAtEnd)
                    {
                        openTailInUse = true;
                    }
                    else
                    {
                        // Sorry, won't fly
                        validator.Trace(outcome, $"Element does not match any slice, but the group at '{Name}' is closed.",
                                        Issue.CONTENT_ELEMENT_FAILS_SLICING_RULE, candidate);
                    }
                }
            }

            // Finally, add any validation items on the elements that made it into the child slices
            foreach (var slice in ChildSlices)
            {
                var sliceOutcome = slice.Validate(validator, errorLocation);
                foreach (var issue in sliceOutcome.Issue)
                {
                    // Only add the issue from the slice outcome if the entry validation did not already catch
                    // the same issue, otherwise the users will see it twice.
                    if (!outcome.Issue.Exists(i => i.Location.First() == issue.Location.First() && i.Details.Text == issue.Details.Text))
                    {
                        outcome.AddIssue(issue);
                    }
                }
            }

            return(outcome);
        }
Exemple #16
0
        public static OperationOutcome ValidateFp(this Validator v, ElementDefinition definition, ScopedNode instance)
        {
            var outcome = new OperationOutcome();

            if (!definition.Constraint.Any())
            {
                return(outcome);
            }
            if (v.Settings.SkipConstraintValidation)
            {
                return(outcome);
            }

            var context = instance.ResourceContext;

            foreach (var constraintElement in definition.Constraint)
            {
                // 20190703 Issue 447 - rng-2 is incorrect in DSTU2 and STU3. EK
                // should be removed from STU3/R4 once we get the new normative version
                // of FP up, which could do comparisons between quantities.
                if (constraintElement.Key == "rng-2")
                {
                    continue;
                }

                bool success = false;

                try
                {
                    var compiled = getExecutableConstraint(v, outcome, instance, constraintElement);
                    success = compiled.Predicate(instance, new FhirEvaluationContext(context)
                    {
                        ElementResolver = callExternalResolver
                    });
                }
                catch (Exception e)
                {
                    v.Trace(outcome, $"Evaluation of FhirPath for constraint '{constraintElement.Key}' failed: {e.Message}",
                            Issue.PROFILE_ELEMENTDEF_INVALID_FHIRPATH_EXPRESSION, instance);
                }

                if (!success)
                {
                    var text  = "Instance failed constraint " + constraintElement.ConstraintDescription();
                    var issue = constraintElement.Severity == ElementDefinition.ConstraintSeverity.Error ?
                                Issue.CONTENT_ELEMENT_FAILS_ERROR_CONSTRAINT : Issue.CONTENT_ELEMENT_FAILS_WARNING_CONSTRAINT;

                    v.Trace(outcome, text, issue, instance);
                }
            }

            return(outcome);

            ITypedElement callExternalResolver(string url)
            {
                OperationOutcome o = new OperationOutcome();
                var result         = v.ExternalReferenceResolutionNeeded(url, o, "dummy");

                if (o.Success && result != null)
                {
                    return(result);
                }

                return(null);
            }
        }
Exemple #17
0
        internal static OperationOutcome ValidateResourceReference(this Validator validator, ScopedNode instance, ElementDefinition.TypeRefComponent typeRef)
        {
            var outcome = new OperationOutcome();

            var reference = instance.ParseResourceReference()?.Reference;

            if (reference == null)       // No reference found -> this is always valid
            {
                return(outcome);
            }


            // Try to resolve the reference *within* the current instance (Bundle, resource with contained resources) first
            var referencedResource = validator.resolveReference(instance, reference,
                                                                out ElementDefinition.AggregationMode? encounteredKind, outcome);

            // Validate the kind of aggregation.
            // If no aggregation is given, all kinds of aggregation are allowed, otherwise only allow
            // those aggregation types that are given in the Aggregation element
            bool hasAggregation = typeRef.Aggregation != null && typeRef.Aggregation.Count() != 0;

            if (hasAggregation && !typeRef.Aggregation.Any(a => a == encounteredKind))
            {
                validator.Trace(outcome, $"Encountered a reference ({reference}) of kind '{encounteredKind}' which is not allowed", Issue.CONTENT_REFERENCE_OF_INVALID_KIND, instance);
            }

            // Bail out if we are asked to follow an *external reference* when this is disabled in the settings
            if (validator.Settings.ResolveExteralReferences == false && encounteredKind == ElementDefinition.AggregationMode.Referenced)
            {
                return(outcome);
            }

            // If we failed to find a referenced resource within the current instance, try to resolve it using an external method
            if (referencedResource == null && encounteredKind == ElementDefinition.AggregationMode.Referenced)
            {
                try
                {
                    referencedResource = validator.ExternalReferenceResolutionNeeded(reference, outcome, instance.Location);
                }
                catch (Exception e)
                {
                    validator.Trace(outcome, $"Resolution of external reference {reference} failed. Message: {e.Message}",
                                    Issue.UNAVAILABLE_REFERENCED_RESOURCE, instance);
                }
            }

            // If the reference was resolved (either internally or externally, validate it
            if (referencedResource != null)
            {
                validator.Trace(outcome, $"Starting validation of referenced resource {reference} ({encounteredKind})", Issue.PROCESSING_START_NESTED_VALIDATION, instance);

                // References within the instance are dealt with within the same validator,
                // references to external entities will operate within a new instance of a validator (and hence a new tracking context).
                // In both cases, the outcome is included in the result.
                OperationOutcome childResult;

                if (encounteredKind != ElementDefinition.AggregationMode.Referenced)
                {
                    childResult = validator.Validate(referencedResource, typeRef.GetDeclaredProfiles(), statedProfiles: null, statedCanonicals: null);
                }
                else
                {
                    var newValidator = validator.NewInstance();
                    childResult = newValidator.Validate(referencedResource, typeRef.GetDeclaredProfiles(), statedProfiles: null, statedCanonicals: null);
                }

                // Prefix each path with the referring resource's path to keep the locations
                // interpretable
                foreach (var issue in childResult.Issue)
                {
                    issue.Location = issue.Location.Concat(new string[] { instance.Location });
                }

                outcome.Include(childResult);
            }
            else
            {
                validator.Trace(outcome, $"Cannot resolve reference {reference}", Issue.UNAVAILABLE_REFERENCED_RESOURCE, instance);
            }

            return(outcome);
        }