示例#1
0
        // This is the one and only main entry point for all external validation calls (i.e. invoked by the user of the API)
        internal OperationOutcome Validate(IElementNavigator instance, string declaredTypeProfile, IEnumerable <string> statedCanonicals, IEnumerable <StructureDefinition> statedProfiles)
        {
            if (!(instance is ScopedNavigator))
            {
                instance = new ScopedNavigator(instance);
            }

            var processor = new ProfilePreprocessor(profileResolutionNeeded, snapshotGenerationNeeded, instance, declaredTypeProfile, statedProfiles, statedCanonicals);
            var outcome   = processor.Process();

            // Note: only start validating if the profiles are complete and consistent
            if (outcome.Success)
            {
                outcome.Add(Validate((ScopedNavigator)instance, processor.Result));
            }

            return(outcome);

            StructureDefinition profileResolutionNeeded(string canonical)
            {
                if (Settings.ResourceResolver != null)
                {
                    return(Settings.ResourceResolver.FindStructureDefinition(canonical));
                }
                else
                {
                    return(null);
                }
            }
        }
示例#2
0
        // This is the one and only main internal entry point for all validations, which in its term
        // will call step 1 in the validator, the function validateElement
        internal OperationOutcome Validate(ScopedNavigator instance, IEnumerable <ElementDefinitionNavigator> definitions)
        {
            var outcome = new OperationOutcome();

            try
            {
                List <ElementDefinitionNavigator> allDefinitions = new List <ElementDefinitionNavigator>(definitions);

                if (allDefinitions.Count() == 1)
                {
                    outcome.Add(validateElement(allDefinitions.Single(), instance));
                }
                else
                {
                    var validators = allDefinitions.Select(nav => createValidator(nav, instance));
                    outcome.Add(this.Combine(BatchValidationMode.All, instance, validators));
                }
            }
            catch (Exception e)
            {
                outcome.AddIssue($"Internal logic failure: {e.Message}", Issue.PROCESSING_CATASTROPHIC_FAILURE, instance);
            }

            return(outcome);
        }
示例#3
0
        internal static OperationOutcome ValidateChildConstraints(this Validator validator, ElementDefinitionNavigator definition,
                                                                  ScopedNavigator 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;
        }
示例#5
0
        public override bool Add(ScopedNavigator instance)
        {
            // Membership of an "element bucket" should be determined by element name
            //var matches = ChildNameMatcher.NameMatches(Root.PathName, candidate);
            //if (!matches)
            //    Validator.Trace(outcome, $"Element name {candidate.Name} does match definition {Root.Path}", Issue.CONTENT_ELEMENT_NAME_DOESNT_MATCH_DEFINITION, candidate);

            Members.Add(instance);
            return(true);
        }
        internal static OperationOutcome ValidateTypeReferences(this Validator validator, 
            IEnumerable<ElementDefinition.TypeRefComponent> typeRefs, ScopedNavigator instance)
        {
            //TODO: It's more efficient to do the non-reference types FIRST, since ANY match would be ok,
            //and validating non-references is cheaper
            //TODO: For each choice, we will currently try to resolve the reference. If it fails, you'll get multiple errors and probably
            //better separate the fetching of the instance from the validation, so we do not run the rest of the validation (multiple times!)
            //when a reference cannot be resolved.  (this happens in a choice type where there are multiple references with multiple profiles)

            IEnumerable<Func<OperationOutcome>> validations = typeRefs.Select(tr => createValidatorForTypeRef(validator, instance,tr));
            return validator.Combine(BatchValidationMode.Any, instance, validations);
        }
        public void TestBucketAssignment()
        {
            var s = createSliceDefs() as SliceGroupBucket;

            var p = new Patient();

            p.Telecom.Add(new ContactPoint {
                System = ContactPoint.ContactPointSystem.Phone, Use = ContactPoint.ContactPointUse.Home, Value = "+31-6-39015765"
            });
            p.Telecom.Add(new ContactPoint {
                System = ContactPoint.ContactPointSystem.Email, Use = ContactPoint.ContactPointUse.Work, Value = "*****@*****.**"
            });
            p.Telecom.Add(new ContactPoint {
                System = ContactPoint.ContactPointSystem.Other, Use = ContactPoint.ContactPointUse.Temp, Value = "skype://crap"
            });
            p.Telecom.Add(new ContactPoint {
                System = ContactPoint.ContactPointSystem.Other, Use = ContactPoint.ContactPointUse.Home, Value = "http://nu.nl"
            });
            p.Telecom.Add(new ContactPoint {
                System = ContactPoint.ContactPointSystem.Fax, Use = ContactPoint.ContactPointUse.Work, Value = "+31-20-6707070"
            });
            var pnav = new ScopedNavigator(new PocoNavigator(p));

            var telecoms = pnav.Children("telecom").Cast <ScopedNavigator>();

            foreach (var telecom in telecoms)
            {
                Assert.True(s.Add(telecom));
            }

            var outcome = s.Validate(_validator, pnav);

            Assert.True(outcome.Success);
            Assert.Equal(0, outcome.Warnings);

            Assert.Equal("+31-6-39015765", s.ChildSlices[0].Members.Single().Children("value").Single().Value);

            var emailBucket = s.ChildSlices[1] as SliceGroupBucket;

            Assert.Equal("*****@*****.**", emailBucket.Members.Single().Children("value").Single().Value);
            Assert.False(emailBucket.ChildSlices[0].Members.Any());
            Assert.Equal("*****@*****.**", emailBucket.ChildSlices[1].Members.Single().Children("value").Single().Value);

            var otherBucket = s.ChildSlices[2] as SliceGroupBucket;

            Assert.Equal("http://nu.nl", otherBucket.ChildSlices[0].Members.Single().Children("value").Single().Value);
            Assert.False(otherBucket.ChildSlices[1].Members.Any());
            Assert.Equal("skype://crap", otherBucket.Members.First().Children("value").Single().Value); // in the open slice - find it on other bucket, not child

            Assert.Equal("+31-20-6707070", s.Members.Last().Children("value").Single().Value);          // in the open-at-end slice
        }
示例#8
0
        public override bool Add(ScopedNavigator candidate)
        {
            var report = Validator.Validate(candidate, Root);

            // If the instance matches everything in the slice, it's definitely a member
            if (report.Success)
            {
                Members.Add(candidate);
                _successes.Add(report);
                return(true);
            }

            // Now, it did not validate against the constraints...
            if (Discriminator?.Any() == true)
            {
                // Get the full path of the discriminator, which is rooted in the current instance path
                var baseInstancePath = candidate.Location;

                // remove all the [num] (from the instance path) and [x] (from the discriminator path) in one go,
                // so a path looking like this remains as a discriminator:  Patient.deceased
                // (note won't work if deceasedBoolean is allowed as a discriminator vlaue)
                var discriminatorPaths = Discriminator.Select(d => strip(baseInstancePath + "." + d)).ToArray();

                if (errorOnDiscriminator(discriminatorPaths, report))
                {
                    // Failed on a discriminator => this instance does not belong to this slice
                    return(false);
                }
                else
                {
                    // Validated against the discriminating elements, instance belongs to this slice although there
                    // are validation errors on other elements
                    Members.Add(candidate);
                    _failures.Add(report);
                    return(true);
                }
            }
            else
            {
                // No discriminator, and validation failed => not a member of this slice
                return(false);
            }
        }
        public static MatchResult Match(ElementDefinitionNavigator definitionParent, ScopedNavigator instanceParent)
        {
            var definitionElements = harvestDefinitionNames(definitionParent);
            var elementsToMatch    = instanceParent.Children().Cast <ScopedNavigator>().ToList();

            List <Match> matches = new List <Match>();

            foreach (var definitionElement in definitionElements)
            {
                var match = new Match()
                {
                    Definition = definitionElement, InstanceElements = new List <ScopedNavigator>()
                };

                // Special case is the .value of a primitive fhir type, this is represented
                // as the "Value" of the IValueProvider interface, not as a real child
                if (definitionElement.Current.IsPrimitiveValueConstraint())
                {
                    if (instanceParent.Value != null)
                    {
                        match.InstanceElements.Add(instanceParent);
                    }
                }
                else
                {
                    var definitionPath = ProfileNavigationExtensions.GetNameFromPath(definitionElement.Current?.Base?.Path ?? definitionElement.Path);
                    var found          = elementsToMatch.Where(ie => NameMatches(definitionPath, ie)).ToList();

                    match.InstanceElements.AddRange(found);
                    elementsToMatch.RemoveAll(e => found.Contains(e));
                }

                matches.Add(match);
            }

            MatchResult result = new MatchResult();

            result.Matches = matches;
            result.UnmatchedInstanceElements = elementsToMatch;

            return(result);
        }
示例#10
0
 public bool Add(ScopedNavigator candidate)
 {
     return(Entry.Add(candidate));
 }
示例#11
0
        internal OperationOutcome ValidateNameReference(ElementDefinition definition, ElementDefinitionNavigator allDefinitions, ScopedNavigator instance)
        {
            var outcome = new OperationOutcome();

            if (definition.NameReference != null)
            {
                Trace(outcome, $"Start validation of constraints referred to by nameReference '{definition.NameReference}'", Issue.PROCESSING_PROGRESS, instance);

                var referencedPositionNav = allDefinitions.ShallowCopy();

                if (referencedPositionNav.JumpToNameReference(definition.NameReference))
                {
                    outcome.Include(Validate(instance, referencedPositionNav));
                }
                else
                {
                    Trace(outcome, $"ElementDefinition uses a non-existing nameReference '{definition.NameReference}'", Issue.PROFILE_ELEMENTDEF_INVALID_NAMEREFERENCE, instance);
                }
            }

            return(outcome);
        }
        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);
            }
        }
示例#13
0
 private Func <OperationOutcome> createValidator(ElementDefinitionNavigator nav, ScopedNavigator instance)
 {
     return(() => validateElement(nav, instance));
 }
示例#14
0
        //   private OperationOutcome validateElement(ElementDefinitionNavigator definition, IElementNavigator instance)

        private OperationOutcome validateElement(ElementDefinitionNavigator definition, ScopedNavigator instance)
        {
            var outcome = new OperationOutcome();

            Trace(outcome, $"Start validation of ElementDefinition at path '{definition.QualifiedDefinitionPath()}'", Issue.PROCESSING_PROGRESS, instance);

            // If navigator cannot be moved to content, there's really nothing to validate against.
            if (definition.AtRoot && !definition.MoveToFirstChild())
            {
                outcome.AddIssue($"Snapshot component of profile '{definition.StructureDefinition?.Url}' has no content.", Issue.PROFILE_ELEMENTDEF_IS_EMPTY, instance);
                return(outcome);
            }

            // Any node must either have a value, or children, or both (e.g. extensions on primitives)
            if (instance.Value == null && !instance.HasChildren())
            {
                outcome.AddIssue("Element must not be empty", Issue.CONTENT_ELEMENT_MUST_HAVE_VALUE_OR_CHILDREN, instance);
                return(outcome);
            }

            var elementConstraints = definition.Current;

            if (elementConstraints.IsPrimitiveValueConstraint())
            {
                // The "value" property of a FHIR Primitive is the bottom of our recursion chain, it does not have a nameReference
                // nor a <type>, the only thing left to do to validate the content is to validate the string representation of the
                // primitive against the regex given in the core definition
                outcome.Add(VerifyPrimitiveContents(elementConstraints, instance));
            }
            else
            {
                bool isInlineChildren = !definition.Current.IsRootElement();

                // Now, validate the children
                if (definition.HasChildren)
                {
                    // If we are at the root of an abstract type (e.g. is this instance a Resource)?
                    // or we are at a nested resource, we may expect more children in the instance than
                    // we know about
                    bool allowAdditionalChildren = (isInlineChildren && elementConstraints.IsResourcePlaceholder()) ||
                                                   (!isInlineChildren && definition.StructureDefinition.Abstract == true);

                    // Handle in-lined constraints on children. In a snapshot, these children should be exhaustive,
                    // so there's no point in also validating the <type> or <nameReference>
                    // TODO: Check whether this is even true when the <type> has a profile?
                    // Note: the snapshot is *not* exhaustive if the declared type is a base FHIR type (like Resource),
                    // in which case there may be additional children (verified in the next step)
                    outcome.Add(this.ValidateChildConstraints(definition, instance, allowAdditionalChildren: allowAdditionalChildren));

                    // Special case: if we are located at a nested resource (i.e. contained or Bundle.entry.resource),
                    // we need to validate based on the actual type of the instance
                    if (isInlineChildren && elementConstraints.IsResourcePlaceholder())
                    {
                        outcome.Add(this.ValidateType(elementConstraints, instance));
                    }
                }

                if (!definition.HasChildren)
                {
                    // No inline-children, so validation depends on the presence of a <type> or <nameReference>
                    if (elementConstraints.Type != null || elementConstraints.NameReference != null)
                    {
                        outcome.Add(this.ValidateType(elementConstraints, instance));
                        outcome.Add(ValidateNameReference(elementConstraints, definition, instance));
                    }
                    else
                    {
                        Trace(outcome, "ElementDefinition has no child, nor does it specify a type or nameReference to validate the instance data against", Issue.PROFILE_ELEMENTDEF_CONTAINS_NO_TYPE_OR_NAMEREF, instance);
                    }
                }
            }

            outcome.Add(this.ValidateFixed(elementConstraints, instance));
            outcome.Add(this.ValidatePattern(elementConstraints, instance));
            outcome.Add(this.ValidateMinMaxValue(elementConstraints, instance));
            outcome.Add(ValidateMaxLength(elementConstraints, instance));
            outcome.Add(this.ValidateFp(elementConstraints, instance));
            outcome.Add(this.ValidateBinding(elementConstraints, instance));
            outcome.Add(this.ValidateExtension(elementConstraints, instance, "http://hl7.org/fhir/StructureDefinition/regex"));

            // If the report only has partial information, no use to show the hierarchy, so flatten it.
            if (Settings.Trace == false)
            {
                outcome.Flatten();
            }

            return(outcome);
        }
示例#15
0
 internal OperationOutcome Validate(ScopedNavigator instance, ElementDefinitionNavigator definition)
 {
     return(Validate(instance, new[] { definition }));
 }
示例#16
0
        private static OperationOutcome ValidateMatch(this Validator validator, Match match, ScopedNavigator 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);
        }
示例#17
0
        public void ResolvePersonWithTwoNameSections()
        {
            FhirPathCompiler compiler = new FhirPathCompiler(CustomFhirPathFunctions.GetSymbolTable());

            Person person = new Person
            {
                Id         = "1",
                Identifier = new List <Identifier>
                {
                    new Identifier
                    {
                        System = "2.16.578.1.12.4.1.4.1",
                        Value  = "16027512345"
                    }
                },
                Name = new List <HumanName>
                {
                    new HumanName
                    {
                        Use    = HumanName.NameUse.Official,
                        Given  = new[] { "Kenneth", },
                        Family = "Myhra"
                    },
                    new HumanName
                    {
                        Use    = HumanName.NameUse.Old,
                        Given  = new[] { "Kenneth" },
                        Family = "AnnetEtternavn"
                    }
                },
                Telecom = new List <ContactPoint>
                {
                    new ContactPoint
                    {
                        Use    = ContactPoint.ContactPointUse.Mobile,
                        System = ContactPoint.ContactPointSystem.Phone,
                        Value  = "93228677"
                    },
                    new ContactPoint
                    {
                        Use    = ContactPoint.ContactPointUse.Home,
                        System = ContactPoint.ContactPointSystem.Phone,
                        Value  = "93228677"
                    },
                    new ContactPoint
                    {
                        Use    = ContactPoint.ContactPointUse.Home,
                        System = ContactPoint.ContactPointSystem.Email,
                        Value  = "*****@*****.**"
                    }
                },
                Address = new List <Address>
                {
                    new Address
                    {
                        Use        = Address.AddressUse.Home,
                        Line       = new [] { "Snipemyrveien 16" },
                        PostalCode = "1273",
                        City       = "Oslo",
                        Country    = "Norway"
                    }
                }
            };

            var personNavigator = new ScopedNavigator(new PocoNavigator(person));

            // Retrieve official name instance,
            // then concatenate given and family separating them by a white space
            IEnumerable <IElementNavigator> result = personNavigator.Select("name.where(use = 'official').select(given & ' ' & family)", compiler: compiler);

            Assert.AreEqual(1, result.Count());
            Assert.AreEqual("Kenneth Myhra", result.Single().Value);

            // Retrieve the distinct values of name and given in a collection for all name instances
            result = personNavigator.Select("(name.given | name.family)", compiler: compiler);
            Assert.AreEqual(3, result.Count());
            Trace.WriteLine(result.ToString());
            Assert.AreEqual("Kenneth", result.First().Value);
            Assert.AreEqual("Myhra", result.Skip(1).First().Value);
            Assert.AreEqual("AnnetEtternavn", result.Skip(2).First().Value);

            // Find identifier that represent FNR
            result = personNavigator.Select("Person.identifier.where(system = '2.16.578.1.12.4.1.4.1').value", compiler: compiler);
            Assert.AreEqual(1, result.Count());
            Assert.AreEqual("16027512345", result.First().Value);

            // Find patient with family equal to 'Myhra'
            result = personNavigator.Select("Person.name.where(family = 'Myhra').family", compiler: compiler);
            Assert.AreEqual(1, result.Count());
            Assert.AreEqual("Myhra", result.First().Value);

            // Retrieve all name instances
            result = personNavigator.Select("Person.name.select(given.join(' ') & ' ' & family)", compiler: compiler);
            Assert.AreEqual(2, result.Count());
            var name = result.First().Value;

            Assert.AreEqual("Kenneth Myhra", name);
            name = result.Skip(1).First().Value;
            Assert.AreEqual("Kenneth AnnetEtternavn", name);

            // Retrieve first name instance
            result = personNavigator.Select("Person.name.select(given & ' ' & family).first()", compiler: compiler);
            Assert.AreEqual(1, result.Count());
            name = result.SingleOrDefault().Value;
            Assert.IsNotNull(name);
            Assert.AreEqual("Kenneth Myhra", name);

            // Retrieve last name instance
            result = personNavigator.Select("Person.name.select(given & ' ' & family).last()", compiler: compiler);
            Assert.AreEqual(1, result.Count());
            name = result.SingleOrDefault().Value;
            Assert.IsNotNull(name);
            Assert.AreEqual("Kenneth AnnetEtternavn", name);

            // Norwegian First name standard
            result = personNavigator.Select("Person.name.where(use = 'official').select(iif(given.count() > 1, given.take(count()-1), given).join(' '))", compiler: compiler);
            Assert.AreEqual(1, result.Count());
            Assert.AreEqual("Kenneth", result.Single().Value);

            // Norwegian middle name standard
            result = personNavigator.Select("Person.name.where(use = 'official').select(iif(given.count() > 1, given.last(), ''))", compiler: compiler);
            Assert.AreEqual(1, result.Count());
            Assert.AreEqual("", result.Single().Value);

            // Family name / surname
            result = personNavigator.Select("Person.name.where(use = 'official').family", compiler: compiler);
            Assert.AreEqual(1, result.Count());
            Assert.AreEqual("Myhra", result.Single().Value);

            // Full name
            result = personNavigator.Select("Person.name.where(use = 'official').select(given.join(' ') & ' ' & family)", compiler: compiler);
            Assert.AreEqual(1, result.Count());
            Assert.AreEqual("Kenneth Myhra", result.Single().Value);

            // Phone number
            result = personNavigator.Select("Person.telecom.where(use = 'home' and system = 'phone').value");
            Assert.AreEqual(1, result.Count());
            Assert.AreEqual("93228677", result.Single().Value);

            // E-mail
            result = personNavigator.Select("Person.telecom.where(use = 'home' and system = 'email').value");
            Assert.AreEqual(1, result.Count());
            Assert.AreEqual("*****@*****.**", result.Single().Value);

            // Adresselinje 1
            result = personNavigator.Select("Person.address.where(use = 'home').line.first()");
            Assert.AreEqual(1, result.Count());
            Assert.AreEqual("Snipemyrveien 16", result.Single().Value);

            // Adresselinje 2
            result = personNavigator.Select("Person.address.where(use = 'home').line.skip(1).first()");
            Assert.AreEqual(0, result.Count());

            // Adresselinje 3
            result = personNavigator.Select("Person.address.where(use = 'home').line.skip(2).first()");
            Assert.AreEqual(0, result.Count());

            // Postnummer
            result = personNavigator.Select("Person.address.where(use = 'home').postalCode");
            Assert.AreEqual(1, result.Count());
            Assert.AreEqual("1273", result.Single().Value);

            // Poststed
            result = personNavigator.Select("Person.address.where(use = 'home').city");
            Assert.AreEqual(1, result.Count());
            Assert.AreEqual("Oslo", result.Single().Value);

            // Land
            result = personNavigator.Select("Person.address.where(use = 'home').country");
            Assert.AreEqual(1, result.Count());
            Assert.AreEqual("Norway", result.Single().Value);

            // --------------

            // person2 has multiple given names.
            Person person2 = new Person
            {
                Id         = "1",
                Identifier = new List <Identifier>
                {
                    new Identifier
                    {
                        System = "2.16.578.1.12.4.1.4.1",
                        Value  = "16027512345"
                    }
                },
                Name = new List <HumanName>
                {
                    new HumanName
                    {
                        Use    = HumanName.NameUse.Official,
                        Given  = new[] { "Lars", "Kristoffer", "Ulstein" },
                        Family = "Jørgensen"
                    }
                }
            };

            var personNavigator2 = new ScopedNavigator(new PocoNavigator(person2));

            // Norwegian First name standard
            result = personNavigator2.Select("Person.name.where(use = 'official').select(iif(given.count() > 1, given.take(count()-1), given).join(' '))", compiler: compiler);
            Assert.AreEqual(1, result.Count());
            Assert.AreEqual("Lars Kristoffer", result.Single().Value);

            // Norwegian middle name standard
            result = personNavigator2.Select("Person.name.where(use = 'official').select(iif(given.count() > 1, given.last(), ''))", compiler: compiler);
            Assert.AreEqual(1, result.Count());
            Assert.AreEqual("Ulstein", result.Single().Value);

            // Family name / surname
            result = personNavigator2.Select("Person.name.where(use = 'official').family", compiler: compiler);
            Assert.AreEqual(1, result.Count());
            Assert.AreEqual("Jørgensen", result.Single().Value);

            // Full name
            result = personNavigator2.Select("Person.name.where(use = 'official').select(given.join(' ') & ' ' & family)", compiler: compiler);
            Assert.AreEqual(1, result.Count());
            Assert.AreEqual("Lars Kristoffer Ulstein Jørgensen", result.Single().Value);
        }
示例#18
0
 public abstract bool Add(ScopedNavigator instance);
示例#19
0
 private static Func <OperationOutcome> createValidatorForTypeRef(Validator validator, ScopedNavigator instance, ElementDefinition.TypeRefComponent tr)
 {
     // In STU3, we need to do BOTH
     // First, call Validate() against the profile (which is then a profile on Reference) THEN validate the referenced resource
     if (tr.Code == FHIRDefinedType.Reference)
     {
         return(() => validator.ValidateResourceReference(instance, tr));
     }
     else
     {
         return(() => validator.Validate(instance, tr.GetDeclaredProfiles(), statedCanonicals: null, statedProfiles: null));
     }
 }
示例#20
0
        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);
        }
示例#21
0
        internal static OperationOutcome ValidateResourceReference(this Validator validator, ScopedNavigator 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);
        }