示例#1
0
        protected override IEnumerable <ISearchValue> Convert(ITypedElement value)
        {
            string code   = value.Scalar("code") as string ?? value.Value as string;
            var    system = value.Scalar("system") as string;

            // From spec: http://hl7.org/fhir/terminologies.html#4.1
            // The instance represents the code only.
            // The system is implicit - it is defined as part of
            // the definition of the element, and not carried in the instance.
            if (string.IsNullOrWhiteSpace(system) && !string.IsNullOrWhiteSpace(code))
            {
                var lookupSystem = _codeSystemResolver.ResolveSystem(value.Location);
                if (!string.IsNullOrWhiteSpace(lookupSystem))
                {
                    system = lookupSystem;
                }
            }

            if (string.IsNullOrEmpty(code) && string.IsNullOrEmpty(system))
            {
                yield return(null);

                yield break;
            }

            yield return(new TokenSearchValue(system, code, null));
        }
        protected override IEnumerable <ISearchValue> Convert(ITypedElement value)
        {
            var lowValue  = (decimal?)value.Scalar("low");
            var highValue = (decimal?)value.Scalar("high");

            if (lowValue != null || highValue != null)
            {
                yield return(new NumberSearchValue(lowValue, highValue));
            }
        }
        protected override IEnumerable <ISearchValue> Convert(ITypedElement value)
        {
            string stringValue = value.Scalar("value") as string;
            string use         = value.Scalar("use") as string;

            if (string.IsNullOrWhiteSpace(stringValue))
            {
                yield break;
            }

            yield return(new TokenSearchValue(use, stringValue, null));
        }
        public void GivenAConformanceBuilder_WhenAddingDefaultInteractions_ThenAuditEventDoesntHaveUpdateDelete()
        {
            _builder.AddDefaultResourceInteractions();

            ITypedElement statement = _builder.Build();

            bool hasCreate = (bool)statement.Scalar($"{ResourceQuery("AuditEvent")}.interaction.where(code = '{TypeRestfulInteraction.Create}').exists()");
            bool noUpdate  = (bool)statement.Scalar($"{ResourceQuery("AuditEvent")}.interaction.where(code = '{TypeRestfulInteraction.Update}').exists()");
            bool noDelete  = (bool)statement.Scalar($"{ResourceQuery("AuditEvent")}.interaction.where(code = '{TypeRestfulInteraction.Delete}').exists()");

            Assert.True(hasCreate);
            Assert.False(noUpdate);
            Assert.False(noDelete);
        }
        protected override IEnumerable <ISearchValue> Convert(ITypedElement value)
        {
            string stringValue = value.Scalar("value") as string;
            string system      = value.Scalar("system") as string;
            string type        = value.Scalar("type.text") as string;

            if (string.IsNullOrEmpty(stringValue))
            {
                yield break;
            }

            // Based on spec: http://hl7.org/fhir/search.html#token,
            // the text for identifier is specified by Identifier.type.text.
            yield return(new TokenSearchValue(system, stringValue, type));
        }
示例#6
0
        public SearchParameterWrapper(ITypedElement searchParameter)
        {
            EnsureArg.IsNotNull(searchParameter, nameof(searchParameter));
            EnsureArg.Is(KnownResourceTypes.SearchParameter, searchParameter.InstanceType, StringComparison.Ordinal, nameof(searchParameter));

            _name        = new Lazy <string>(() => searchParameter.Scalar("name")?.ToString());
            _description = new Lazy <string>(() => searchParameter.Scalar("description")?.ToString());
            _url         = new Lazy <string>(() => searchParameter.Scalar("url")?.ToString());
            _expression  = new Lazy <string>(() => searchParameter.Scalar("expression")?.ToString());
            _type        = new Lazy <string>(() => searchParameter.Scalar("type")?.ToString());

            _base      = new Lazy <IReadOnlyList <string> >(() => searchParameter.Select("base")?.AsStringValues().ToArray());
            _component = new Lazy <IReadOnlyList <ITypedElement> >(() => searchParameter.Select("component")?.ToArray());
            _target    = new Lazy <IReadOnlyList <string> >(() => searchParameter.Select("target")?.AsStringValues().ToArray());
        }
        protected override IEnumerable <ISearchValue> Convert(ITypedElement value)
        {
            var startString = value.Scalar("start")?.ToString();
            var endString   = value.Scalar("end")?.ToString();

            PartialDateTime start = string.IsNullOrWhiteSpace(startString) ?
                                    PartialDateTime.MinValue :
                                    PartialDateTime.Parse(startString);

            PartialDateTime end = string.IsNullOrWhiteSpace(endString) ?
                                  PartialDateTime.MaxValue :
                                  PartialDateTime.Parse(endString);

            yield return(new DateTimeSearchValue(start, end));
        }
示例#8
0
        protected override IEnumerable <ISearchValue> Convert(ITypedElement value)
        {
            var highValue = (ITypedElement)value.Scalar("high");
            var lowValue  = (ITypedElement)value.Scalar("low");

            var quantityRepresentativeValue = lowValue ?? highValue;

            if (quantityRepresentativeValue != null)
            {
                var system = quantityRepresentativeValue.Scalar("system") as string;
                var code   = quantityRepresentativeValue.Scalar("code") as string;

                // FROM https://www.hl7.org/fhir/datatypes.html#Range: "The unit and code/system elements of the low or high elements SHALL match."
                yield return(new QuantitySearchValue(system, code, (decimal?)lowValue?.Scalar("value"), (decimal?)highValue?.Scalar("value")));
            }
        }
示例#9
0
        protected override IEnumerable <ISearchValue> Convert(ITypedElement value)
        {
            var highValue = (decimal?)value.Scalar("high.value");
            var lowValue  = (decimal?)value.Scalar("low.value");

            var quantityRepresentativeValue = (lowValue != null) ? "low" : (highValue != null) ? "high" : null;

            if (quantityRepresentativeValue != null)
            {
                var system = value.Scalar($"{quantityRepresentativeValue}.system") as string;
                var code   = value.Scalar($"{quantityRepresentativeValue}.code") as string;

                // FROM https://www.hl7.org/fhir/datatypes.html#Range: "The unit and code/system elements of the low or high elements SHALL match."
                yield return(new QuantitySearchValue(system, code, lowValue, highValue));
            }
        }
示例#10
0
        private IEnumerable <ValidationFailure> ValidateResource(ITypedElement typedElement)
        {
            EnsureArg.IsNotNull(typedElement, nameof(typedElement));

            var xhtml = typedElement.Scalar(KnownFhirPaths.ResourceNarrative) as string;

            if (string.IsNullOrEmpty(xhtml))
            {
                yield break;
            }

            var errors       = _narrativeHtmlSanitizer.Validate(xhtml);
            var fullFhirPath = typedElement.InstanceType + "." + KnownFhirPaths.ResourceNarrative;

            foreach (var error in errors)
            {
                yield return(new FhirValidationFailure(
                                 fullFhirPath,
                                 error,
                                 new OperationOutcomeIssue(
                                     OperationOutcomeConstants.IssueType.Structure,
                                     OperationOutcomeConstants.IssueSeverity.Error,
                                     error,
                                     location: new[] { fullFhirPath })));
            }
        }
        protected override IEnumerable <ISearchValue> Convert(ITypedElement value)
        {
            // Based on spec: http://hl7.org/fhir/search.html#token,
            // CodeableConcept.text is searchable, but we will only create a dedicated entry for it
            // if it is different from the display text of one of its codings

            string text = value.Scalar("text") as string;
            bool   conceptTextNeedsToBeAdded = !string.IsNullOrWhiteSpace(text);

            foreach (ITypedElement coding in value.Select("coding"))
            {
                TokenSearchValue searchValue = coding?.ToTokenSearchValue();

                if (searchValue != null)
                {
                    if (conceptTextNeedsToBeAdded)
                    {
                        conceptTextNeedsToBeAdded = !string.Equals(text, searchValue.Text, StringComparison.OrdinalIgnoreCase);
                    }

                    yield return(searchValue);
                }
            }

            if (conceptTextNeedsToBeAdded)
            {
                yield return(new TokenSearchValue(null, null, text));
            }
        }
        public static TokenSearchValue ToTokenSearchValue(this ITypedElement coding)
        {
            EnsureArg.IsNotNull(coding, nameof(coding));

            string system  = coding.Scalar("system") as string;
            string code    = coding.Scalar("code") as string;
            string display = coding.Scalar("display") as string;

            if (!string.IsNullOrWhiteSpace(system) ||
                !string.IsNullOrWhiteSpace(code) ||
                !string.IsNullOrWhiteSpace(display))
            {
                return(new TokenSearchValue(system, code, display));
            }

            return(null);
        }
示例#13
0
        protected override IEnumerable <ISearchValue> Convert(ITypedElement value)
        {
            var decimalValue = (decimal?)value.Scalar("value");

            if (decimalValue == null)
            {
                yield break;
            }

            var system = value.Scalar("system")?.ToString();
            var code   = value.Scalar("code")?.ToString();

            yield return(new QuantitySearchValue(
                             system,
                             code,
                             decimalValue.GetValueOrDefault()));
        }
        public void GivenAConformanceBuilder_WhenAddingDefaultInteractions_ThenParameterTypeIsNotAdded()
        {
            _builder.AddDefaultResourceInteractions();

            ITypedElement statement = _builder.Build();

            bool noParameters = (bool)statement.Scalar($"{ResourceQuery(KnownResourceTypes.Parameters)}.exists()");

            Assert.False(noParameters);
        }
        public void GivenAConformanceBuilder_WhenAddingRestSearchParam_ThenTypeSearchParamIsAdded()
        {
            _builder.AddDefaultRestSearchParams();

            ITypedElement statement = _builder.Build();

            object typeDefinition = statement.Scalar($"CapabilityStatement.rest.searchParam.where(name = '_type').definition");

            Assert.Equal("http://hl7.org/fhir/SearchParameter/type", typeDefinition.ToString());
        }
        public void GivenAConformanceBuilder_WhenAddingDefaultInteractions_ThenProfileIsAddedAtResource()
        {
            _builder.PopulateDefaultResourceInteractions();

            ITypedElement statement = _builder.Build();

            object profile = statement.Scalar($"{ResourceQuery("Account")}.profile");

            Assert.Equal("http://hl7.org/fhir/StructureDefinition/Account", profile);
        }
示例#17
0
        protected override IEnumerable <ISearchValue> Convert(ITypedElement value)
        {
            var reference = value.Scalar("reference") as ITypedElement;

            if (reference == null)
            {
                return(Enumerable.Empty <ISearchValue>());
            }

            return(_referenceSearchValueParser.ConvertTo(reference));
        }
        protected override IEnumerable <ISearchValue> Convert(ITypedElement value)
        {
            var concept = value.Scalar("concept") as ITypedElement;

            if (concept == null)
            {
                return(Enumerable.Empty <ISearchValue>());
            }

            return(_converter.ConvertTo(concept));
        }
        public void GivenAConformanceBuilder_WhenExecutingScalar_ThenCorrectInformationIsReturned()
        {
            string httpMicrosoftCom = "http://microsoft.com";

            _builder.Update(x => x.Url = new Uri(httpMicrosoftCom));

            ITypedElement statement = _builder.Build();

            object url = statement.Scalar("Resource.url");

            Assert.Equal(httpMicrosoftCom, url);
        }
        protected override IEnumerable <ISearchValue> Convert(ITypedElement value)
        {
            var decimalValue = (decimal?)value.Scalar("value");

            if (decimalValue == null)
            {
                yield break;
            }

            if (ModelInfoProvider.Version == FhirSpecification.Stu3)
            {
                var code   = value.Scalar("code")?.ToString();
                var system = value.Scalar("system")?.ToString();

                // The spec specifies that only the code value must be provided.
                if (code == null)
                {
                    yield break;
                }

                yield return(new QuantitySearchValue(
                                 system,
                                 code,
                                 decimalValue.GetValueOrDefault()));
            }
            else
            {
                var currency = value.Scalar("currency")?.ToString();

                if (currency == null)
                {
                    yield break;
                }

                yield return(new QuantitySearchValue(
                                 CurrencyValueSet.CodeSystemUri, // TODO: Use ICodeSystemResolver to pull this from resourcepath-codesystem-mappings.json once it's added.
                                 currency,
                                 decimalValue.GetValueOrDefault()));
            }
        }
        public void GivenAConformanceBuilder_WhenAddingResourceSearchParam_ThenTypeSearchParamIsNotAddedUnderResource()
        {
            _searchParameterDefinitionManager.GetSearchParameters("Account")
            .Returns(new[] { new SearchParameterInfo("_type", "_type", SearchParamType.Token, description: "description"), });

            _builder.AddDefaultSearchParameters();

            ITypedElement statement = _builder.Build();

            object typeName = statement.Scalar($"{ResourceQuery("Account")}.searchParam.where(name = '_type').name");

            Assert.Null(typeName);
        }
        public void GivenAConformanceBuilder_WhenAddingSupportedProfile_ThenSupportedProfilePresent()
        {
            string profile = "coolProfile";

            _supportedProfiles.GetSupportedProfiles("Account").Returns(new[] { profile });
            _builder.PopulateDefaultResourceInteractions().SyncProfiles();
            ITypedElement statement = _builder.Build();
            string        fhirPath  = ModelInfoProvider.Version == FhirSpecification.Stu3
                ? $"CapabilityStatement.profile.where(reference='{profile}').exists()"
                : $"{ResourceQuery("Account")}.supportedProfile.first()='{profile}'";
            var profileFound = (bool)statement.Scalar(fhirPath);

            Assert.True(profileFound);
        }
示例#23
0
        protected override IEnumerable <ISearchValue> Convert(ITypedElement value)
        {
            IEnumerable <ITypedElement> givenNames = value.Select("given");
            IEnumerable <ITypedElement> prefixes   = value.Select("prefix");
            IEnumerable <ITypedElement> suffixes   = value.Select("suffix");
            var family = value.Scalar("family") as string;
            var text   = value.Scalar("text") as string;

            // https://www.hl7.org/fhir/patient.html recommends the following:
            // A server defined search that may match any of the string fields in the HumanName, including family, give, prefix, suffix, suffix, and/or text
            // we will do a basic search based on family or given or prefix or suffix or text for now. Details on localization will be handled later.
            foreach (var given in givenNames.AsStringValues())
            {
                yield return(new StringSearchValue(given));
            }

            if (!string.IsNullOrWhiteSpace(family))
            {
                yield return(new StringSearchValue(family));
            }

            foreach (var prefix in prefixes.AsStringValues())
            {
                yield return(new StringSearchValue(prefix));
            }

            foreach (var suffix in suffixes.AsStringValues())
            {
                yield return(new StringSearchValue(suffix));
            }

            if (!string.IsNullOrWhiteSpace(text))
            {
                yield return(new StringSearchValue(text));
            }
        }
        public void GivenAConformanceBuilder_WhenAddingDefaultSearchParameters_ThenDocumentationIsAdded()
        {
            string description = "Logical id of this artifact";

            _searchParameterDefinitionManager.GetSearchParameters("Account")
            .Returns(new[] { new SearchParameterInfo("_id", "_id", SearchParamType.Token, description: description), });

            _builder.AddDefaultSearchParameters();

            ITypedElement statement = _builder.Build();

            object idDocumentation = statement.Scalar($"{ResourceQuery("Account")}.searchParam.where(name = '_id').documentation");

            Assert.Equal(description, idDocumentation);
        }
        protected override IEnumerable <ISearchValue> Convert(ITypedElement value)
        {
            var reference = value.Scalar("reference") as string;

            if (reference == null)
            {
                yield break;
            }

            // Contained resources will not be searchable.
            if (reference.StartsWith("#", StringComparison.Ordinal) ||
                reference.StartsWith("urn:", StringComparison.Ordinal))
            {
                yield break;
            }

            yield return(_referenceSearchValueParser.Parse(reference));
        }
        protected override IEnumerable <ISearchValue> Convert(ITypedElement value)
        {
            // http://hl7.org/fhir/patient.html recommends the following:
            // A server defined search that may match any of the string fields in the Address, including line, city, state, country, postalCode, and/or text.
            // we will do a basic search based on any of the address component for now. Details on localization will be handled later.

            var city     = value.Scalar("city") as string;
            var country  = value.Scalar("country") as string;
            var district = value.Scalar("district") as string;
            IEnumerable <ITypedElement> lines = value.Select("line");
            var postCode = value.Scalar("postalCode") as string;
            var state    = value.Scalar("state") as string;
            var text     = value.Scalar("text") as string;

            if (!string.IsNullOrWhiteSpace(city))
            {
                yield return(new StringSearchValue(city));
            }

            if (!string.IsNullOrWhiteSpace(country))
            {
                yield return(new StringSearchValue(country));
            }

            if (!string.IsNullOrWhiteSpace(district))
            {
                yield return(new StringSearchValue(district));
            }

            foreach (var line in lines.AsStringValues())
            {
                yield return(new StringSearchValue(line));
            }

            if (!string.IsNullOrWhiteSpace(postCode))
            {
                yield return(new StringSearchValue(postCode));
            }

            if (!string.IsNullOrWhiteSpace(state))
            {
                yield return(new StringSearchValue(state));
            }

            if (!string.IsNullOrWhiteSpace(text))
            {
                yield return(new StringSearchValue(text));
            }
        }
        public static string GetStringScalar(this ITypedElement resource, string propertyName)
        {
            EnsureArg.IsNotNull(propertyName, nameof(propertyName));

            return(resource.Scalar(propertyName)?.ToString());
        }
示例#28
0
        private static List <(string ResourceType, SearchParameterInfo SearchParameter)> ValidateAndGetFlattenedList(
            IReadOnlyCollection <ITypedElement> searchParamCollection,
            IDictionary <Uri, SearchParameterInfo> uriDictionary,
            IModelInfoProvider modelInfoProvider)
        {
            var issues           = new List <OperationOutcomeIssue>();
            var searchParameters = searchParamCollection.ToList();

            // Do the first pass to make sure all resources are SearchParameter.
            for (int entryIndex = 0; entryIndex < searchParameters.Count; entryIndex++)
            {
                var searchParameterElement = searchParameters[entryIndex];

                // Make sure resources are not null and they are SearchParameter.
                if (searchParameterElement == null || !string.Equals(searchParameterElement.InstanceType, KnownResourceTypes.SearchParameter, StringComparison.OrdinalIgnoreCase))
                {
                    AddIssue(Core.Resources.SearchParameterDefinitionInvalidResource, entryIndex);
                    continue;
                }

                var searchParameter = new SearchParameterWrapper(searchParameterElement);

                try
                {
                    SearchParameterInfo searchParameterInfo = GetOrCreateSearchParameterInfo(searchParameter, uriDictionary);
                    uriDictionary.Add(new Uri(searchParameter.Url), searchParameterInfo);
                }
                catch (FormatException)
                {
                    AddIssue(Core.Resources.SearchParameterDefinitionInvalidDefinitionUri, entryIndex);
                    continue;
                }
                catch (ArgumentException)
                {
                    AddIssue(Core.Resources.SearchParameterDefinitionDuplicatedEntry, searchParameter.Url);
                    continue;
                }
            }

            EnsureNoIssues();

            var validatedSearchParameters = new List <(string ResourceType, SearchParameterInfo SearchParameter)>
            {
                // _type is currently missing from the search params definition bundle, so we inject it in here.
                (KnownResourceTypes.Resource, new SearchParameterInfo(SearchParameterNames.ResourceType, SearchParamType.Token, SearchParameterNames.ResourceTypeUri, null, "Resource.type().name", null)),
            };

            // Do the second pass to make sure the definition is valid.
            foreach (var searchParameterElement in searchParameters)
            {
                var searchParameter = new SearchParameterWrapper(searchParameterElement);

                // If this is a composite search parameter, then make sure components are defined.
                if (string.Equals(searchParameter.Type, SearchParamType.Composite.GetLiteral(), StringComparison.OrdinalIgnoreCase))
                {
                    var composites = searchParameter.Component;
                    if (composites.Count == 0)
                    {
                        AddIssue(Core.Resources.SearchParameterDefinitionInvalidComponent, searchParameter.Url);
                        continue;
                    }

                    SearchParameterInfo compositeSearchParameter = GetOrCreateSearchParameterInfo(searchParameter, uriDictionary);

                    for (int componentIndex = 0; componentIndex < composites.Count; componentIndex++)
                    {
                        ITypedElement component     = composites[componentIndex];
                        var           definitionUrl = GetComponentDefinition(component);

                        if (definitionUrl == null ||
                            !uriDictionary.TryGetValue(new Uri(definitionUrl), out SearchParameterInfo componentSearchParameter))
                        {
                            AddIssue(
                                Core.Resources.SearchParameterDefinitionInvalidComponentReference,
                                searchParameter.Url,
                                componentIndex);
                            continue;
                        }

                        if (componentSearchParameter.Type == SearchParamType.Composite)
                        {
                            AddIssue(
                                Core.Resources.SearchParameterDefinitionComponentReferenceCannotBeComposite,
                                searchParameter.Url,
                                componentIndex);
                            continue;
                        }

                        if (string.IsNullOrWhiteSpace(component.Scalar("expression")?.ToString()))
                        {
                            AddIssue(
                                Core.Resources.SearchParameterDefinitionInvalidComponentExpression,
                                searchParameter.Url,
                                componentIndex);
                            continue;
                        }

                        compositeSearchParameter.Component[componentIndex].ResolvedSearchParameter = componentSearchParameter;
                    }
                }

                // Make sure the base is defined.
                var bases = searchParameter.Base;
                if (bases.Count == 0)
                {
                    AddIssue(Core.Resources.SearchParameterDefinitionBaseNotDefined, searchParameter.Url);
                    continue;
                }

                for (int baseElementIndex = 0; baseElementIndex < bases.Count; baseElementIndex++)
                {
                    var code = bases[baseElementIndex];

                    string baseResourceType = code;

                    // Make sure the expression is not empty unless they are known to have empty expression.
                    // These are special search parameters that searches across all properties and needs to be handled specially.
                    if (ShouldExcludeEntry(baseResourceType, searchParameter.Name, modelInfoProvider) ||
                        (modelInfoProvider.Version == FhirSpecification.R5 && _knownBrokenR5.Contains(new Uri(searchParameter.Url))))
                    {
                        continue;
                    }
                    else
                    {
                        if (string.IsNullOrWhiteSpace(searchParameter.Expression))
                        {
                            AddIssue(Core.Resources.SearchParameterDefinitionInvalidExpression, searchParameter.Url);
                            continue;
                        }
                    }

                    validatedSearchParameters.Add((baseResourceType, GetOrCreateSearchParameterInfo(searchParameter, uriDictionary)));
                }
            }

            EnsureNoIssues();

            return(validatedSearchParameters);

            void AddIssue(string format, params object[] args)
            {
                issues.Add(new OperationOutcomeIssue(
                               OperationOutcomeConstants.IssueSeverity.Fatal,
                               OperationOutcomeConstants.IssueType.Invalid,
                               string.Format(CultureInfo.InvariantCulture, format, args)));
            }

            void EnsureNoIssues()
            {
                if (issues.Count != 0)
                {
                    throw new InvalidDefinitionException(
                              Core.Resources.SearchParameterDefinitionContainsInvalidEntry,
                              issues.ToArray());
                }
            }
        }
示例#29
0
 private static string GetComponentDefinition(ITypedElement component)
 {
     // In Stu3 the Url is under 'definition.reference'
     return(component.Scalar("definition.reference")?.ToString() ??
            component.Scalar("definition")?.ToString());
 }
示例#30
0
        private static object scalar(string expr)
        {
            ITypedElement dummy = ElementNode.ForPrimitive(true);

            return(dummy.Scalar(expr));
        }