internal SearchParameterInfo(SearchParameterWrapper wrapper) { var components = wrapper.Component .Select(x => new SearchParameterComponentInfo( new Uri(GetComponentDefinition(x)), x.Scalar("expression")?.ToString())) .ToArray(); SearchParamType searchParamType = EnumUtility.ParseLiteral <SearchParamType>(wrapper.Type) .GetValueOrDefault(); Name = wrapper.Name; Code = wrapper.Code; Type = searchParamType; Url = new Uri(wrapper.Url); Expression = wrapper.Expression; Description = wrapper.Description; Component = components; TargetResourceTypes = wrapper.Target; BaseResourceTypes = wrapper.Base; string GetComponentDefinition(ITypedElement component) { // In Stu3 the Url is under 'definition.reference' return(component.Scalar("definition.reference")?.ToString() ?? component.Scalar("definition")?.ToString()); } }
private static SearchParameterInfo GetOrCreateSearchParameterInfo(SearchParameterWrapper searchParameter, IDictionary <Uri, SearchParameterInfo> uriDictionary) { // Return SearchParameterInfo that has already been created for this Uri if (uriDictionary.TryGetValue(new Uri(searchParameter.Url), out var spi)) { return(spi); } var components = searchParameter.Component .Select(x => new SearchParameterComponentInfo( new Uri(GetComponentDefinition(x)), x.Scalar("expression")?.ToString())) .ToArray(); SearchParamType searchParamType = EnumUtility.ParseLiteral <SearchParamType>(searchParameter.Type) .GetValueOrDefault(); var sp = new SearchParameterInfo( searchParameter.Name, searchParamType, new Uri(searchParameter.Url), expression: searchParameter.Expression, description: searchParameter.Description, components: components, targetResourceTypes: searchParameter.Target, baseResourceTypes: searchParameter.Base); return(sp); }
public async Task DeleteSearchParameterAsync(RawResource searchParamResource) { try { var searchParam = _modelInfoProvider.ToTypedElement(searchParamResource); var searchParameterWrapper = new SearchParameterWrapper(searchParam); // First we delete the status metadata from the data store as this fuction depends on the // the in memory definition manager. Once complete we remove the SearchParameter from // the definition manager. await _searchParameterStatusManager.DeleteSearchParameterStatusAsync(searchParameterWrapper.Url); _searchParameterDefinitionManager.DeleteSearchParameter(searchParam); } catch (FhirException fex) { fex.Issues.Add(new OperationOutcomeIssue( OperationOutcomeConstants.IssueSeverity.Error, OperationOutcomeConstants.IssueType.Exception, Core.Resources.CustomSearchDeleteError)); throw; } catch (Exception ex) { var customSearchException = new ConfigureCustomSearchException(Core.Resources.CustomSearchDeleteError); customSearchException.Issues.Add(new OperationOutcomeIssue( OperationOutcomeConstants.IssueSeverity.Error, OperationOutcomeConstants.IssueType.Exception, ex.Message)); throw customSearchException; } }
public async Task AddSearchParameterAsync(ITypedElement searchParam) { try { _searchParameterDefinitionManager.AddNewSearchParameters(new List <ITypedElement>() { searchParam }); var searchParameterWrapper = new SearchParameterWrapper(searchParam); await _searchParameterStatusManager.AddSearchParameterStatusAsync(new List <string>() { searchParameterWrapper.Url }); } catch (FhirException fex) { fex.Issues.Add(new OperationOutcomeIssue( OperationOutcomeConstants.IssueSeverity.Error, OperationOutcomeConstants.IssueType.Exception, Core.Resources.CustomSearchCreateError)); throw; } catch (Exception ex) { var customSearchException = new ConfigureCustomSearchException(Core.Resources.CustomSearchCreateError); customSearchException.Issues.Add(new OperationOutcomeIssue( OperationOutcomeConstants.IssueSeverity.Error, OperationOutcomeConstants.IssueType.Exception, ex.Message)); throw customSearchException; } }
public async Task UpdateSearchParameterAsync(ITypedElement searchParam, RawResource previousSearchParam) { try { // We need to make sure we have the latest search parameters before trying to update // existing search parameter. This is to avoid trying to update a search parameter that // was recently added and that hasn't propogated to all fhir-server instances. await GetAndApplySearchParameterUpdates(CancellationToken.None); var searchParameterWrapper = new SearchParameterWrapper(searchParam); var searchParameterInfo = new SearchParameterInfo(searchParameterWrapper); (bool Supported, bool IsPartiallySupported)supportedResult = _searchParameterSupportResolver.IsSearchParameterSupported(searchParameterInfo); if (!supportedResult.Supported) { throw new SearchParameterNotSupportedException(searchParameterInfo.Url); } // check data store specific support for SearchParameter if (!_dataStoreSearchParameterValidator.ValidateSearchParameter(searchParameterInfo, out var errorMessage)) { throw new SearchParameterNotSupportedException(errorMessage); } var prevSearchParam = _modelInfoProvider.ToTypedElement(previousSearchParam); var prevSearchParamUrl = prevSearchParam.GetStringScalar("url"); // As any part of the SearchParameter may have been changed, including the URL // the most reliable method of updating the SearchParameter is to delete the previous // data and insert the updated version await _searchParameterStatusManager.DeleteSearchParameterStatusAsync(prevSearchParamUrl); _searchParameterDefinitionManager.DeleteSearchParameter(prevSearchParam); _searchParameterDefinitionManager.AddNewSearchParameters(new List <ITypedElement>() { searchParam }); await _searchParameterStatusManager.AddSearchParameterStatusAsync(new List <string>() { searchParameterWrapper.Url }); } catch (FhirException fex) { fex.Issues.Add(new OperationOutcomeIssue( OperationOutcomeConstants.IssueSeverity.Error, OperationOutcomeConstants.IssueType.Exception, Core.Resources.CustomSearchUpdateError)); throw; } catch (Exception ex) { var customSearchException = new ConfigureCustomSearchException(Core.Resources.CustomSearchUpdateError); customSearchException.Issues.Add(new OperationOutcomeIssue( OperationOutcomeConstants.IssueSeverity.Error, OperationOutcomeConstants.IssueType.Exception, ex.Message)); throw customSearchException; } }
public async Task AddSearchParameterAsync(ITypedElement searchParam, CancellationToken cancellationToken) { try { // verify the parameter is supported before continuing var searchParameterWrapper = new SearchParameterWrapper(searchParam); var searchParameterInfo = new SearchParameterInfo(searchParameterWrapper); if (searchParameterInfo.Component?.Any() == true) { foreach (SearchParameterComponentInfo c in searchParameterInfo.Component) { c.ResolvedSearchParameter = _searchParameterDefinitionManager.GetSearchParameter(c.DefinitionUrl.OriginalString); } } (bool Supported, bool IsPartiallySupported)supportedResult = _searchParameterSupportResolver.IsSearchParameterSupported(searchParameterInfo); if (!supportedResult.Supported) { throw new SearchParameterNotSupportedException(searchParameterInfo.Url); } // check data store specific support for SearchParameter if (!_dataStoreSearchParameterValidator.ValidateSearchParameter(searchParameterInfo, out var errorMessage)) { throw new SearchParameterNotSupportedException(errorMessage); } _logger.LogTrace("Adding the search parameter '{url}'", searchParameterWrapper.Url); _searchParameterDefinitionManager.AddNewSearchParameters(new List <ITypedElement> { searchParam }); await _searchParameterStatusManager.AddSearchParameterStatusAsync(new List <string> { searchParameterWrapper.Url }, cancellationToken); } catch (FhirException fex) { fex.Issues.Add(new OperationOutcomeIssue( OperationOutcomeConstants.IssueSeverity.Error, OperationOutcomeConstants.IssueType.Exception, Core.Resources.CustomSearchCreateError)); throw; } catch (Exception ex) { var customSearchException = new ConfigureCustomSearchException(Core.Resources.CustomSearchCreateError); customSearchException.Issues.Add(new OperationOutcomeIssue( OperationOutcomeConstants.IssueSeverity.Error, OperationOutcomeConstants.IssueType.Exception, ex.Message)); throw customSearchException; } }
private static SearchParameterInfo GetOrCreateSearchParameterInfo(SearchParameterWrapper searchParameter, IDictionary <Uri, SearchParameterInfo> uriDictionary) { // Return SearchParameterInfo that has already been created for this Uri if (uriDictionary.TryGetValue(new Uri(searchParameter.Url), out var spi)) { return(spi); } return(new SearchParameterInfo(searchParameter)); }
public async Task AddSearchParameterAsync(ITypedElement searchParam) { try { // verify the parameter is supported before continuing var searchParameterWrapper = new SearchParameterWrapper(searchParam); var searchParameterInfo = new SearchParameterInfo(searchParameterWrapper); (bool Supported, bool IsPartiallySupported)supportedResult = _searchParameterSupportResolver.IsSearchParameterSupported(searchParameterInfo); if (!supportedResult.Supported) { throw new SearchParameterNotSupportedException(searchParameterInfo.Url); } // check data store specific support for SearchParameter if (!_dataStoreSearchParameterValidator.ValidateSearchParameter(searchParameterInfo, out var errorMessage)) { throw new SearchParameterNotSupportedException(errorMessage); } _searchParameterDefinitionManager.AddNewSearchParameters(new List <ITypedElement>() { searchParam }); var searchParameterUrl = searchParam.GetStringScalar("url"); await _searchParameterStatusManager.AddSearchParameterStatusAsync(new List <string>() { searchParameterWrapper.Url }); } catch (FhirException fex) { fex.Issues.Add(new OperationOutcomeIssue( OperationOutcomeConstants.IssueSeverity.Error, OperationOutcomeConstants.IssueType.Exception, Core.Resources.CustomSearchCreateError)); throw; } catch (Exception ex) { var customSearchException = new ConfigureCustomSearchException(Core.Resources.CustomSearchCreateError); customSearchException.Issues.Add(new OperationOutcomeIssue( OperationOutcomeConstants.IssueSeverity.Error, OperationOutcomeConstants.IssueType.Exception, ex.Message)); throw customSearchException; } }
public async Task UpdateSearchParameterAsync(ITypedElement searchParam, RawResource prevSearchParamRaw) { try { var searchParameterWrapper = new SearchParameterWrapper(searchParam); var searchParameterInfo = new SearchParameterInfo(searchParameterWrapper); (bool Supported, bool IsPartiallySupported)supportedResult = _searchParameterSupportResolver.IsSearchParameterSupported(searchParameterInfo); if (!supportedResult.Supported) { throw new SearchParameterNotSupportedException(searchParameterInfo.Url); } var prevSearchParam = _modelInfoProvider.ToTypedElement(prevSearchParamRaw); var prevSearchParamUrl = prevSearchParam.GetStringScalar("url"); // As any part of the SearchParameter may have been changed, including the URL // the most reliable method of updating the SearchParameter is to delete the previous // data and insert the updated version await _searchParameterStatusManager.DeleteSearchParameterStatusAsync(prevSearchParamUrl); _searchParameterDefinitionManager.DeleteSearchParameter(prevSearchParam); _searchParameterDefinitionManager.AddNewSearchParameters(new List <ITypedElement>() { searchParam }); await _searchParameterStatusManager.AddSearchParameterStatusAsync(new List <string>() { searchParameterWrapper.Url }); } catch (FhirException fex) { fex.Issues.Add(new OperationOutcomeIssue( OperationOutcomeConstants.IssueSeverity.Error, OperationOutcomeConstants.IssueType.Exception, Core.Resources.CustomSearchUpdateError)); throw; } catch (Exception ex) { var customSearchException = new ConfigureCustomSearchException(Core.Resources.CustomSearchUpdateError); customSearchException.Issues.Add(new OperationOutcomeIssue( OperationOutcomeConstants.IssueSeverity.Error, OperationOutcomeConstants.IssueType.Exception, ex.Message)); throw customSearchException; } }
public void DeleteSearchParameter(ITypedElement searchParam) { var searchParamWrapper = new SearchParameterWrapper(searchParam); SearchParameterInfo searchParameterInfo = null; if (!UrlLookup.TryRemove(new Uri(searchParamWrapper.Url), out searchParameterInfo)) { throw new Exception(string.Format(Resources.CustomSearchParameterNotfound, searchParamWrapper.Url)); } var allResourceTypes = searchParameterInfo.TargetResourceTypes.Union(searchParameterInfo.BaseResourceTypes); foreach (var resourceType in allResourceTypes) { TypeLookup[resourceType].TryRemove(searchParameterInfo.Code, out var removedParam); } CalculateSearchParameterHash(); }
public void DeleteSearchParameter(ITypedElement searchParam) { var searchParamWrapper = new SearchParameterWrapper(searchParam); DeleteSearchParameter(searchParamWrapper.Url); }
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()); } } }