private async Task NormalSearchProcessing(KeyValuePair <string, StringValues> Parameter) { List <Bug.Logic.DomainModel.SearchParameter> SearchParameterList = await ISearchParameterCache.GetForIndexingAsync(this.FhirVersion, this.ResourceContext); //Remove modifiers var SearchParameterName = Parameter.Key.Split(FhirSearchQuery.TermSearchModifierDelimiter)[0].Trim(); Bug.Logic.DomainModel.SearchParameter SearchParameter = SearchParameterList.SingleOrDefault(x => x.Name == SearchParameterName); if (SearchParameter != null) { IList <ISearchQueryBase> SearchQueryBaseList = await ISearchQueryFactory.Create(this.ResourceContext, SearchParameter, Parameter); foreach (ISearchQueryBase SearchQueryBase in SearchQueryBaseList) { if (SearchQueryBase.IsValid) { Outcome !.SearchQueryList.Add(SearchQueryBase); } else { Outcome !.InvalidSearchQueryList.Add(new InvalidSearchQueryParameter(SearchQueryBase.RawValue, SearchQueryBase.InvalidMessage)); } } } else { foreach (var ParamValue in Parameter.Value) { string Message = $"The search query parameter: {Parameter.Key} is not supported by this server for the resource type: {ResourceContext.GetCode()}, the whole parameter was : {Parameter.Key}={ParamValue}"; Outcome !.UnsupportedSearchQueryList.Add(new InvalidSearchQueryParameter(Parameter.Key, ParamValue, Message)); } } }
public async Task <ChainQueryProcessingOutcome> Process(FhirVersion fhirVersion, ResourceType resourceContext, KeyValuePair <string, StringValues> Parameter) { this.FhirVersion = fhirVersion; this.ResourceContext = resourceContext; var Outcome = new ChainQueryProcessingOutcome(); this.RawParameter = $"{Parameter.Key}={Parameter.Value}"; string[] ChaimedParameterSplit = Parameter.Key.Split(FhirSearchQuery.TermChainDelimiter); for (int i = 0; i < ChaimedParameterSplit.Length; i++) { //Each segment in the chain is a IsChainedReferance except the last segment in the chain which has the value. bool IsChainedReferance = !(i == ChaimedParameterSplit.Length - 1); Bug.Logic.DomainModel.SearchParameter?SearchParameter = null; string ParameterNameWithModifier = Parameter.Key.Split(FhirSearchQuery.TermChainDelimiter)[i]; StringValues ParameterValue = string.Empty; //There is no valid Value for a chained reference parameter unless it is the last in a series //of chains, so don't set it unless this is the last parameter in the whole chain. if (i == ChaimedParameterSplit.Count() - 1) { ParameterValue = Parameter.Value; } var SingleChainedParameter = new KeyValuePair <string, StringValues>(ParameterNameWithModifier, ParameterValue); string ParameterName = string.Empty; string ParameterModifierTypedResource = string.Empty; //Check for and deal with modifiers e.g 'Patient' in the example: subject:Patient.family=millar if (ParameterNameWithModifier.Contains(FhirSearchQuery.TermSearchModifierDelimiter)) { string[] ParameterModifierSplit = ParameterNameWithModifier.Split(FhirSearchQuery.TermSearchModifierDelimiter); ParameterName = ParameterModifierSplit[0].Trim(); if (ParameterModifierSplit.Length > 1) { ResourceType?ModifierResourceType = IResourceTypeSupport.GetTypeFromName(ParameterModifierSplit[1].Trim()); if (ModifierResourceType.HasValue && IKnownResource.IsKnownResource(this.FhirVersion, ParameterModifierSplit[1].Trim())) { ParameterModifierTypedResource = ParameterModifierSplit[1].Trim(); } else { ErrorInSearchParameterProcessing = true; //If the Parent is ok then we can assume that any error further down the chain is an invalid search term rather than an unsupported term //as it is clear that this is a FHIR search term and not some other search parameter forgen to FHIR if (ParentChainSearchParameter is object) { InvalidSearchQueryParameterList.Add(new InvalidSearchQueryParameter(this.RawParameter, $"The resource type modifier: {ParameterModifierSplit[1].Trim()} within the chained search query of {this.RawParameter} is not a known resource for FHIR version: {this.FhirVersion.GetCode()} within this server")); } else { //Here we are only looking up the ParameterName to check weather this should be an unsupported parameter or an invalid parameter. //If we know the ParameterName then it is invalid whereas if we don't then it is unsupported and both are not known. List <Bug.Logic.DomainModel.SearchParameter> TempSearchParameterList = await ISearchParameterCache.GetForIndexingAsync(this.FhirVersion, this.ResourceContext); var TempSearchParameter = TempSearchParameterList.SingleOrDefault(x => x.Name == ParameterName); if (TempSearchParameter is null) { string Message = $"Both the search parameter name: {ParameterName} for the resource type: {this.ResourceContext.GetCode()} and its resource type modifier: {ParameterModifierSplit[1].Trim()} within the chained search query of {this.RawParameter} are unsupported within this server for FHIR version: {this.FhirVersion.GetCode()}."; UnsupportedSearchQueryParameterList.Add(new InvalidSearchQueryParameter(this.RawParameter, Message)); } else { InvalidSearchQueryParameterList.Add(new InvalidSearchQueryParameter(this.RawParameter, $"The resource type modifier: {ParameterModifierSplit[1].Trim()} within the chained search query of {this.RawParameter} is not a known resource type for this server and FHIR version {this.FhirVersion.GetCode()}.")); } } break; } } } else { ParameterName = ParameterNameWithModifier; } SearchParameter = await GetSearchParameter(ParameterName); //We have no resolved a SearchParameter so we parse the value if it is the end of the chain, see IsChainedReferance //or we are only parsing as a chain segment with no end value if (SearchParameter != null) { //If this is the last parameter in the chain then treat is as not a chain, otherwise treat as a chain await SetChain(SearchParameter, SingleChainedParameter, IsChainedReferance); } else { ErrorInSearchParameterProcessing = true; if (this.InvalidSearchQueryParameterList.Count == 0 && this.UnsupportedSearchQueryParameterList.Count == 0) { throw new ApplicationException("Internal Server Error: When processing a chain search query we failed to resolve a search parameter for the query string however their are " + $"no items found in either the {nameof(InvalidSearchQueryParameterList)} or the {nameof(UnsupportedSearchQueryParameterList)}. This is an error in its self."); } break; } } //End of Chain loop if (!ErrorInSearchParameterProcessing) { if (ParentChainSearchParameter is object) { Outcome !.SearchQueryList.Add(ParentChainSearchParameter); return(Outcome); } else { throw new NullReferenceException(nameof(PreviousChainSearchParameter)); } } else { InvalidSearchQueryParameterList.ForEach(x => Outcome !.InvalidSearchQueryList.Add(x)); UnsupportedSearchQueryParameterList.ForEach(x => Outcome !.UnsupportedSearchQueryList.Add(x)); return(Outcome); } }
private async Task <SearchQueryHas?> ProccessHas(HasParameter Has, string RawHasParameter) { var Result = new SearchQueryHas(); ResourceType?TargetResourceForSearchQuery = IResourceTypeSupport.GetTypeFromName(Has.TargetResourceForSearchQuery); if (TargetResourceForSearchQuery.HasValue && IKnownResource.IsKnownResource(this.FhirVersion, Has.TargetResourceForSearchQuery)) { Result.TargetResourceForSearchQuery = TargetResourceForSearchQuery.Value; } else { Outcome !.InvalidSearchQueryList.Add(new InvalidSearchQueryParameter(RawHasParameter, $"The resource type name of: {Has.TargetResourceForSearchQuery} in a {FhirSearchQuery.TermHas} parameter could not be resolved to a resource type supported by this server for FHIR version {this.FhirVersion.GetCode()}.")); return(null); } List <Bug.Logic.DomainModel.SearchParameter> SearchParameterList = await ISearchParameterCache.GetForIndexingAsync(this.FhirVersion, Result.TargetResourceForSearchQuery); Bug.Logic.DomainModel.SearchParameter BackReferenceSearchParameter = SearchParameterList.SingleOrDefault(x => x.Name == Has.BackReferenceSearchParameterName); if (BackReferenceSearchParameter is object && BackReferenceSearchParameter.SearchParamTypeId == SearchParamType.Reference) { Result.BackReferenceSearchParameter = BackReferenceSearchParameter; } else { if (BackReferenceSearchParameter is null) { string Message = $"The reference search parameter back to the target resource type of: {Has.BackReferenceSearchParameterName} is not a supported search parameter for the resource type {this.ResourceContext.GetCode()} for FHIR version {this.FhirVersion.GetCode()} within this server."; Outcome !.InvalidSearchQueryList.Add(new InvalidSearchQueryParameter(RawHasParameter, Message)); return(null); } } if (Has.ChildHasParameter is object) { Result.ChildSearchQueryHas = await ProccessHas(Has.ChildHasParameter, RawHasParameter); return(Result); } else { if (Has.SearchQuery.HasValue) { SearchParameterList = await ISearchParameterCache.GetForIndexingAsync(this.FhirVersion, Result.TargetResourceForSearchQuery); Bug.Logic.DomainModel.SearchParameter SearchParameter = SearchParameterList.SingleOrDefault(x => x.Name == Has.SearchQuery.Value.Key); if (SearchParameter is object) { IList <ISearchQueryBase> SearchQueryBaseList = await ISearchQueryFactory.Create(this.ResourceContext, SearchParameter, Has.SearchQuery.Value); if (SearchQueryBaseList.Count == 1) { if (SearchQueryBaseList[0].IsValid) { Result.SearchQuery = SearchQueryBaseList[0]; return(Result); } else { string Message = $"Error parsing the search parameter found at the end of a {FhirSearchQuery.TermHas} query. The search parameter name was : {Has.SearchQuery.Value.Key} with the value of {Has.SearchQuery.Value.Value}. " + $"Additional information: {SearchQueryBaseList[0].InvalidMessage}"; Outcome !.InvalidSearchQueryList.Add(new InvalidSearchQueryParameter(RawHasParameter, Message)); return(null); } } else { throw new ApplicationException($"The {FhirSearchQuery.TermHas} parameter seems to end with more then one search parameter, this should not be possible."); } } else { string Message = $"The {FhirSearchQuery.TermHas} query finish with a search parameter: {Has.SearchQuery.Value.Key} for the resource type of: {Result.TargetResourceForSearchQuery.GetCode()}. " + $"However, the search parameter: {Has.SearchQuery.Value.Key} is not a supported search parameter for this resource type in this server for FHIR version {this.FhirVersion.GetCode()}."; Outcome !.InvalidSearchQueryList.Add(new InvalidSearchQueryParameter(RawHasParameter, Message)); return(null); } } else { string Message = $"The {FhirSearchQuery.TermHas} query does not finish with a search parameter and value."; Outcome !.InvalidSearchQueryList.Add(new InvalidSearchQueryParameter(RawHasParameter, Message)); return(null); } } }
private async Task ProcessIncludeSearchParameters(IList <IncludeParameter> IncludeList) { if (IncludeList != null) { foreach (var Include in IncludeList) { bool ParseOk = true; string RawParameter = $"{IncludeType.Include.GetCode()}:{FhirSearchQuery.TermIncludeRecurse}={Include.Value}"; //if (this.FhirVersion == FhirVersion.Stu3 && Include.Recurse) //{ // if (Include.Type == IncludeType.Include) // { // string IncorrectRawParameter = $"{IncludeType.Include.GetCode()}:{FhirSearchQuery.TermIncludeRecurse}={Include.Value}"; // string CorrectRawParameter = $"{IncludeType.Include.GetCode()}:{FhirSearchQuery.TermIncludeRecurse}={Include.Value}"; // string Message = $"The Include type query of: {IncorrectRawParameter} is using the FHIR R4 {FhirSearchQuery.TermIncludeRecurse} yet this is a FHIR Stu3 search which must use {FhirSearchQuery.TermIncludeIterate} to achieve the same effect. " + // $"Please update your query to use the FHIR Stu3 for such as: {CorrectRawParameter} "; // Outcome!.InvalidSearchQueryList.Add(new InvalidSearchQueryParameter(IncorrectRawParameter, Message)); // ParseOk = false; // } //} //else if (this.FhirVersion == FhirVersion.R4 && Include.Iterate) //{ // string IncorrectRawParameter = $"{IncludeType.Include.GetCode()}:{FhirSearchQuery.TermIncludeIterate}={Include.Value}"; // string CorrectRawParameter = $"{IncludeType.Include.GetCode()}:{FhirSearchQuery.TermIncludeRecurse}={Include.Value}"; // string Message = $"The Include type query of: {IncorrectRawParameter} is using the FHIR Stu3 {FhirSearchQuery.TermIncludeIterate} yet this is a FHIR R4 search which must use {FhirSearchQuery.TermIncludeRecurse} to achieve the same effect. " + // $"Please update your query to use the FHIR R4 term such as: {CorrectRawParameter} "; // Outcome!.InvalidSearchQueryList.Add(new InvalidSearchQueryParameter(IncorrectRawParameter, Message)); // ParseOk = false; //} SearchQueryInclude SearchParameterInclude = new SearchQueryInclude(Include.Type) { IsIterate = Include.Iterate, IsRecurse = Include.Recurse }; var valueSplitArray = Include.Value.Split(FhirSearchQuery.TermSearchModifierDelimiter); string ScourceResource = valueSplitArray[0].Trim(); ResourceType?ScourceResourceType = IResourceTypeSupport.GetTypeFromName(ScourceResource); if (ScourceResourceType.HasValue && IKnownResource.IsKnownResource(this.FhirVersion, ScourceResource)) { SearchParameterInclude.SourceResourceType = ScourceResourceType.Value; } else { ParseOk = false; Outcome !.InvalidSearchQueryList.Add(new InvalidSearchQueryParameter(RawParameter, $"The source resource type of: {ScourceResource} for the {Include.Type.GetCode()} parameter is not recognized as a FHIR {this.FhirVersion.GetCode()} resource type by this server.")); break; } if (valueSplitArray.Count() > 2) { string TargetResourceTypeString = valueSplitArray[2].Trim(); //checked we have a something if we don't then that is fine just a syntax error of the callers part //i.e _includes=Patient:subject: if (!string.IsNullOrWhiteSpace(TargetResourceTypeString)) { ResourceType?TargetResourceType = IResourceTypeSupport.GetTypeFromName(TargetResourceTypeString); if (TargetResourceType.HasValue && IKnownResource.IsKnownResource(this.FhirVersion, TargetResourceTypeString)) { SearchParameterInclude.SearchParameterTargetResourceType = TargetResourceType.Value; } else { ParseOk = false; Outcome !.InvalidSearchQueryList.Add(new InvalidSearchQueryParameter(RawParameter, $"The target resource type of : {TargetResourceTypeString} for the {Include.Type.GetCode()} parameter is not recognized as a FHIR {this.FhirVersion.GetCode()} resource type by this server.")); break; } } } if (valueSplitArray.Count() > 1) { string SearchTerm = valueSplitArray[1].Trim(); List <DomainModel.SearchParameter> SearchParameterList = await ISearchParameterCache.GetForIndexingAsync(this.FhirVersion, SearchParameterInclude.SourceResourceType.Value); if (SearchTerm == "*") { if (SearchParameterInclude.SearchParameterTargetResourceType is object) { SearchParameterInclude.SearchParameterList = SearchParameterList.Where(x => x.SearchParamTypeId == SearchParamType.Reference && x.TargetResourceTypeList.Any(v => v.ResourceTypeId == SearchParameterInclude.SearchParameterTargetResourceType.Value)).ToList(); } else { SearchParameterInclude.SearchParameterList = SearchParameterList.Where(x => x.SearchParamTypeId == SearchParamType.Reference).ToList(); } } else { DomainModel.SearchParameter SearchParameter = SearchParameterList.SingleOrDefault(x => x.Name == SearchTerm); if (SearchParameter != null) { if (SearchParameter.SearchParamTypeId == SearchParamType.Reference) { if (SearchParameter.TargetResourceTypeList != null && SearchParameterInclude.SearchParameterTargetResourceType.HasValue) { if (SearchParameter.TargetResourceTypeList.SingleOrDefault(x => x.ResourceTypeId == SearchParameterInclude.SearchParameterTargetResourceType.Value) == null) { ParseOk = false; string Message = $"The target Resource '{SearchParameterInclude.SearchParameterTargetResourceType.Value.GetCode()}' of the _includes parameter is not recognized for the source '{SearchParameterInclude.SourceResourceType.GetCode()}' Resource's search parameter {SearchParameter.Name}."; Outcome !.InvalidSearchQueryList.Add(new InvalidSearchQueryParameter(RawParameter, Message)); break; } } SearchParameterInclude.SearchParameterList = new List <DomainModel.SearchParameter> { SearchParameter }; } else { ParseOk = false; string Message = $"The source Resource '{SearchParameterInclude.SourceResourceType.GetCode()}' search parameter '{SearchParameter.Name}' of the _includes parameter is not of search parameter of type Reference, found search parameter type of '{SearchParameter.SearchParamTypeId.GetCode()}'."; Outcome !.InvalidSearchQueryList.Add(new InvalidSearchQueryParameter(RawParameter, Message)); break; } } else { ParseOk = false; string Message = $"The source Resource '{SearchParameterInclude.SourceResourceType.GetCode()}' search parameter '{SearchTerm}' is not a valid search parameter for the source Resource type."; Outcome !.InvalidSearchQueryList.Add(new InvalidSearchQueryParameter(RawParameter, Message)); break; } } } //All Ok so add as valid include if (ParseOk) { Outcome !.IncludeList.Add(SearchParameterInclude); } } } }