private ResourceElement CreateBundle(SearchResult result, Bundle.BundleType type, Func <SearchResultEntry, Bundle.EntryComponent> selectionFunction) { EnsureArg.IsNotNull(result, nameof(result)); // Create the bundle from the result. var bundle = new Bundle(); if (_fhirRequestContextAccessor.FhirRequestContext.BundleIssues.Any()) { var operationOutcome = new OperationOutcome { Issue = new List <OperationOutcome.IssueComponent>(_fhirRequestContextAccessor.FhirRequestContext.BundleIssues.Select(x => x.ToPoco())), }; bundle.Entry.Add(new Bundle.EntryComponent { Resource = operationOutcome, Search = new Bundle.SearchComponent { Mode = Bundle.SearchEntryMode.Outcome, }, }); } IEnumerable <Bundle.EntryComponent> entries = result.Results.Select(selectionFunction); bundle.Entry.AddRange(entries); if (result.ContinuationToken != null) { bundle.NextLink = _urlResolver.ResolveRouteUrl( result.UnsupportedSearchParameters, result.UnsupportedSortingParameters, Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(result.ContinuationToken)), true); } if (result.Partial) { // if the query resulted in a partial indication, add appropriate outcome // as an entry var resource = new OperationOutcome(); resource.Issue = new List <OperationOutcome.IssueComponent>(); resource.Issue.Add(new OperationOutcome.IssueComponent { Severity = OperationOutcome.IssueSeverity.Warning, Code = OperationOutcome.IssueType.Incomplete, Diagnostics = Core.Resources.TruncatedIncludeMessage, }); bundle.Entry.Add(new Bundle.EntryComponent() { Resource = resource, Search = new Bundle.SearchComponent { Mode = Bundle.SearchEntryMode.Outcome, }, }); } // Add the self link to indicate which search parameters were used. bundle.SelfLink = _urlResolver.ResolveRouteUrl(result.UnsupportedSearchParameters, result.UnsupportedSortingParameters); bundle.Id = _fhirRequestContextAccessor.FhirRequestContext.CorrelationId; bundle.Type = type; bundle.Total = result?.TotalCount; bundle.Meta = new Meta { LastUpdated = Clock.UtcNow, }; return(bundle.ToResourceElement()); }
public async Task <SearchResult> SearchHistoryAsync( string resourceType, string resourceId, PartialDateTime at, PartialDateTime since, PartialDateTime before, int?count, string continuationToken, CancellationToken cancellationToken) { var queryParameters = new List <Tuple <string, string> >(); if (at != null) { if (since != null) { // _at and _since cannot be both specified. throw new InvalidSearchOperationException( string.Format( CultureInfo.InvariantCulture, Core.Resources.AtCannotBeSpecifiedWithBeforeOrSince, KnownQueryParameterNames.At, KnownQueryParameterNames.Since)); } if (before != null) { // _at and _since cannot be both specified. throw new InvalidSearchOperationException( string.Format( CultureInfo.InvariantCulture, Core.Resources.AtCannotBeSpecifiedWithBeforeOrSince, KnownQueryParameterNames.At, KnownQueryParameterNames.Before)); } } if (before != null) { var beforeOffset = before.ToDateTimeOffset( defaultMonth: 1, defaultDaySelector: (year, month) => 1, defaultHour: 0, defaultMinute: 0, defaultSecond: 0, defaultFraction: 0.0000000m, defaultUtcOffset: TimeSpan.Zero).ToUniversalTime(); if (beforeOffset.CompareTo(Clock.UtcNow) > 0) { // you cannot specify a value for _before in the future throw new InvalidSearchOperationException( string.Format( CultureInfo.InvariantCulture, Core.Resources.HistoryParameterBeforeCannotBeFuture, KnownQueryParameterNames.Before)); } } bool searchByResourceId = !string.IsNullOrEmpty(resourceId); if (searchByResourceId) { queryParameters.Add(Tuple.Create(SearchParameterNames.Id, resourceId)); } if (!string.IsNullOrEmpty(continuationToken)) { queryParameters.Add(Tuple.Create(KnownQueryParameterNames.ContinuationToken, continuationToken)); } if (at != null) { queryParameters.Add(Tuple.Create(SearchParameterNames.LastUpdated, at.ToString())); } else { if (since != null) { queryParameters.Add(Tuple.Create(SearchParameterNames.LastUpdated, $"ge{since}")); } if (before != null) { queryParameters.Add(Tuple.Create(SearchParameterNames.LastUpdated, $"lt{before}")); } } if (count.HasValue && count > 0) { queryParameters.Add(Tuple.Create(KnownQueryParameterNames.Count, count.ToString())); } SearchOptions searchOptions = _searchOptionsFactory.Create(resourceType, queryParameters); SearchResult searchResult = await SearchHistoryInternalAsync(searchOptions, cancellationToken); // If no results are returned from the _history search // determine if the resource actually exists or if the results were just filtered out. // The 'deleted' state has no effect because history will return deleted resources if (searchByResourceId && searchResult.Results.Any() == false) { var resource = await _fhirDataStore.GetAsync(new ResourceKey(resourceType, resourceId), cancellationToken); if (resource == null) { throw new ResourceNotFoundException(string.Format(Core.Resources.ResourceNotFoundById, resourceType, resourceId)); } } return(searchResult); }