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)); }
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)); }
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"))); } }
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)); } }
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); }
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); }
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); }
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()); }
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()); } } }
private static string GetComponentDefinition(ITypedElement component) { // In Stu3 the Url is under 'definition.reference' return(component.Scalar("definition.reference")?.ToString() ?? component.Scalar("definition")?.ToString()); }
private static object scalar(string expr) { ITypedElement dummy = ElementNode.ForPrimitive(true); return(dummy.Scalar(expr)); }