Ejemplo n.º 1
0
        /// <summary>
        /// Searches a resource from the client registry datastore
        /// </summary>
        public Bundle SearchResource(string resourceType)
        {
            this.ThrowIfNotReady();

            // Get the services from the service registry
            var auditService = ApplicationContext.Current.GetService(typeof(IAuditorService)) as IAuditorService;

            // Stuff for auditing and exception handling
            AuditData            audit   = null;
            List <IResultDetail> details = new List <IResultDetail>();
            FhirQueryResult      result  = null;

            try
            {
                // Get query parameters
                var queryParameters   = WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters;
                var resourceProcessor = FhirResourceHandlerUtil.GetResourceHandler(resourceType);

                // Setup outgoing content
                WebOperationContext.Current.OutgoingRequest.Headers.Add("Last-Modified", DateTime.Now.ToString("ddd, dd MMM yyyy HH:mm:ss zzz"));

                if (resourceProcessor == null) // Unsupported resource
                {
                    throw new FileNotFoundException();
                }

                // TODO: Appropriately format response
                // Process incoming request
                result = resourceProcessor.Query(WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters);

                if (result == null || result.Outcome == ResultCode.Rejected)
                {
                    throw new InvalidDataException("Message was rejected");
                }
                else if (result.Outcome != ResultCode.Accepted)
                {
                    throw new DataException("Query failed");
                }

                audit = AuditUtil.CreateAuditData(result.Results);
                // Create the Atom feed
                return(MessageUtil.CreateBundle(result));
            }
            catch (Exception e)
            {
                audit         = AuditUtil.CreateAuditData(null);
                audit.Outcome = OutcomeIndicator.EpicFail;
                return(this.ErrorHelper(e, result, true) as Bundle);
            }
            finally
            {
                if (auditService != null)
                {
                    auditService.SendAudit(audit);
                }
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Query data from the client registry
        /// </summary>
        public SVC.Messaging.FHIR.FhirQueryResult Query(System.Collections.Specialized.NameValueCollection parameters)
        {
            FhirQueryResult result = new FhirQueryResult();

            result.Details = new List <IResultDetail>();

            // Get query parameters
            var resourceProcessor = FhirMessageProcessorUtil.GetMessageProcessor(this.ResourceName);

            // Process incoming request

            NameValueCollection goodParameters = new NameValueCollection();

            for (int i = 0; i < parameters.Count; i++)
            {
                for (int v = 0; v < parameters.GetValues(i).Length; v++)
                {
                    if (!String.IsNullOrEmpty(parameters.GetValues(i)[v]))
                    {
                        goodParameters.Add(parameters.GetKey(i), MessageUtil.Escape(parameters.GetValues(i)[v]));
                    }
                }
            }
            parameters = goodParameters;

            var queryObject = resourceProcessor.ParseQuery(goodParameters, result.Details);

            result.Query = queryObject;

            // sanity check
#if !DEBUG
            if (result.Query.ActualParameters.Count == 0)
            {
                result.Outcome = ResultCode.Rejected;
                result.Details.Add(new ValidationResultDetail(ResultDetailType.Error, ApplicationContext.LocalizationService.GetString("MSGE077"), null, null));
            }
            else
#endif
            if (result.Details.Exists(o => o.Type == ResultDetailType.Error))
            {
                result.Outcome = ResultCode.Error;
                result.Details.Add(new ResultDetail(ResultDetailType.Error, ApplicationContext.LocalizationService.GetString("MSGE00A"), null, null));
            }
            else if (queryObject.Filter == null || result.Outcome != ResultCode.Accepted)
            {
                throw new InvalidOperationException("Could not process query parameters!");
            }
            else
            {
                result = DataUtil.Query(queryObject, result.Details);
            }

            return(result);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Reads the complete history of the specified identifier
        /// </summary>
        public Bundle History(string id)
        {
            if (String.IsNullOrEmpty(id))
            {
                this.m_traceSource.TraceError($"Argument {nameof(id)} null or empty");
                throw new ArgumentNullException(this.m_localizationService.GetString("error.type.ArgumentNullException"));
            }

            Guid guidId = Guid.Empty;

            if (!Guid.TryParse(id, out guidId))
            {
                throw new ArgumentException(this.m_localizationService.FormatString("error.type.ArgumentException", new
                {
                    param = "id"
                }));
            }

            var result = this.Read(guidId, Guid.Empty);

            if (result == null)
            {
                throw new KeyNotFoundException(this.m_localizationService.GetString("error.type.KeyNotFoundException"));
            }

            // Results
            List <TModel> results = new List <TModel>()
            {
                result
            };

            while ((result as IVersionedEntity)?.PreviousVersionKey.HasValue == true)
            {
                result = this.Read(guidId, (result as IVersionedEntity).PreviousVersionKey.Value);
                results.Add(result);
            }

            // FHIR Operation result
            var retVal = new FhirQueryResult(typeof(TFhirResource).Name)
            {
                Results = results.Select(this.MapToFhir).Select(o => new Bundle.EntryComponent()
                {
                    Resource = o,
                    Response = new Bundle.ResponseComponent()
                    {
                        Status = "200"
                    }
                }).ToList()
            };

            return(ExtensionUtil.ExecuteBeforeSendResponseBehavior(TypeRestfulInteraction.HistoryInstance, this.ResourceType, MessageUtil.CreateBundle(retVal, Bundle.BundleType.History)) as Bundle);
        }
        /// <summary>
        /// Parameters
        /// </summary>
        public override Bundle Query(NameValueCollection parameters)
        {
            if (parameters == null)
            {
                this.m_tracer.TraceError(nameof(parameters));
                throw new ArgumentNullException(nameof(parameters), this.m_localizationService.GetString("error.type.ArgumentNullException"));
            }

            Core.Model.Query.NameValueCollection hdsiQuery = null;
            var query = QueryRewriter.RewriteFhirQuery(typeof(Observation), typeof(Core.Model.Acts.Observation), parameters, out hdsiQuery);

            // Do the query
            var totalResults = 0;

            IEnumerable <Core.Model.Acts.Observation> hdsiResults = null;

            if (parameters["value-concept"] != null)
            {
                var predicate = QueryExpressionParser.BuildLinqExpression <CodedObservation>(hdsiQuery);
                hdsiResults = this.QueryEx(predicate, query.QueryId, query.Start, query.Quantity, out totalResults).OfType <Core.Model.Acts.Observation>();
            }
            else if (parameters["value-quantity"] != null)
            {
                var predicate = QueryExpressionParser.BuildLinqExpression <QuantityObservation>(hdsiQuery);
                hdsiResults = this.QueryEx(predicate, query.QueryId, query.Start, query.Quantity, out totalResults).OfType <Core.Model.Acts.Observation>();
            }
            else
            {
                var predicate = QueryExpressionParser.BuildLinqExpression <Core.Model.Acts.Observation>(hdsiQuery);
                hdsiResults = this.Query(predicate, query.QueryId, query.Start, query.Quantity, out totalResults);
            }

            // Return FHIR query result
            var retVal = new FhirQueryResult("Observation")
            {
                Results = hdsiResults.Select(this.MapToFhir).Select(o => new Bundle.EntryComponent
                {
                    Resource = o,
                    Search   = new Bundle.SearchComponent
                    {
                        Mode = Bundle.SearchEntryMode.Match
                    }
                }).ToList(),
                Query        = query,
                TotalResults = totalResults
            };

            base.ProcessIncludes(hdsiResults, parameters, retVal);

            return(ExtensionUtil.ExecuteBeforeSendResponseBehavior(TypeRestfulInteraction.SearchType, this.ResourceType, MessageUtil.CreateBundle(retVal, Bundle.BundleType.Searchset)) as Bundle);
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Queries for a specified resource.
        /// </summary>
        /// <param name="parameters">The parameters.</param>
        /// <returns>Returns the FHIR query result containing the results of the query.</returns>
        /// <exception cref="System.ArgumentNullException">parameters</exception>
        public virtual Bundle Query(System.Collections.Specialized.NameValueCollection parameters)
        {
            if (parameters == null)
            {
                this.m_traceSource.TraceError($"Argument {nameof(parameters)} null or empty");
                throw new ArgumentNullException(this.m_localizationService.GetString("error.type.ArgumentNullException"));
            }

            Core.Model.Query.NameValueCollection hdsiQuery = null;
            FhirQuery query = QueryRewriter.RewriteFhirQuery(typeof(TFhirResource), typeof(TModel), parameters, out hdsiQuery);

            // Do the query
            int totalResults = 0;
            var predicate    = QueryExpressionParser.BuildLinqExpression <TModel>(hdsiQuery);
            var hdsiResults  = this.Query(predicate, query.QueryId, query.Start, query.Quantity, out totalResults);

            var auth = AuthenticationContext.Current;

            // Return FHIR query result
            if (Environment.ProcessorCount > 4)
            {
                hdsiResults = hdsiResults.AsParallel().AsOrdered();
            }

            var retVal = new FhirQueryResult(typeof(TFhirResource).Name)
            {
                Results = hdsiResults.Select(o =>
                {
                    using (AuthenticationContext.EnterContext(auth.Principal))
                    {
                        return(new Bundle.EntryComponent()
                        {
                            Resource = this.MapToFhir(o),
                            Search = new Bundle.SearchComponent()
                            {
                                Mode = Bundle.SearchEntryMode.Match
                            }
                        });
                    }
                }).ToList(),
                Query        = query,
                TotalResults = totalResults
            };

            this.ProcessIncludes(hdsiResults, parameters, retVal);

            return(ExtensionUtil.ExecuteBeforeSendResponseBehavior(TypeRestfulInteraction.SearchType, this.ResourceType, MessageUtil.CreateBundle(retVal, Bundle.BundleType.Searchset)) as Bundle);
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Create a feed
        /// </summary>
        internal static Bundle CreateBundle(FhirOperationResult result)
        {
            Bundle          retVal      = new Bundle();
            FhirQueryResult queryResult = result as FhirQueryResult;

            int pageNo = queryResult == null || queryResult.Query.Quantity == 0 ? 0 : queryResult.Query.Start / queryResult.Query.Quantity,
                nPages = queryResult == null || queryResult.Query.Quantity == 0 ? 1 : (queryResult.TotalResults / queryResult.Query.Quantity);

            retVal.Type = BundleType.SearchResults;

            retVal.Id = String.Format("urn:uuid:{0}", Guid.NewGuid());

            // Make the Self uri
            String baseUri = WebOperationContext.Current.IncomingRequest.UriTemplateMatch.RequestUri.AbsoluteUri;

            if (baseUri.Contains("?"))
            {
                baseUri = baseUri.Substring(0, baseUri.IndexOf("?") + 1);
            }
            else
            {
                baseUri += "?";
            }

            // Self uri
            if (queryResult != null)
            {
                for (int i = 0; i < queryResult.Query.ActualParameters.Count; i++)
                {
                    foreach (var itm in queryResult.Query.ActualParameters.GetValues(i))
                    {
                        switch (queryResult.Query.ActualParameters.GetKey(i))
                        {
                        case "_stateid":
                        case "_page":
                            break;

                        default:
                            baseUri += string.Format("{0}={1}&", queryResult.Query.ActualParameters.GetKey(i), itm);
                            break;
                        }
                    }
                }

                if (!baseUri.Contains("_stateid=") && queryResult.Query.QueryId != Guid.Empty)
                {
                    baseUri += String.Format("_stateid={0}&", queryResult.Query.QueryId);
                }
            }

            // Format
            string format = WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters["_format"];

            if (String.IsNullOrEmpty(format))
            {
                format = "xml";
            }
            else if (format == "application/xml+fhir")
            {
                format = "xml";
            }
            else if (format == "application/json+fhir")
            {
                format = "json";
            }

            if (!baseUri.Contains("_format"))
            {
                baseUri += String.Format("_format={0}&", format);
            }

            var localizationService = ApplicationContext.Current.GetService <ILocalizationService>();

            // Self URI
            if (queryResult != null && queryResult.TotalResults > queryResult.Results.Count)
            {
                retVal.Link.Add(new BundleLink(new Uri(String.Format("{0}_page={1}", baseUri, pageNo)), "self"));
                if (pageNo > 0)
                {
                    retVal.Link.Add(new BundleLink(new Uri(String.Format("{0}_page=0", baseUri)), "first"));
                    retVal.Link.Add(new BundleLink(new Uri(String.Format("{0}_page={1}", baseUri, pageNo - 1)), "previous"));
                }
                if (pageNo <= nPages)
                {
                    retVal.Link.Add(new BundleLink(new Uri(String.Format("{0}_page={1}", baseUri, pageNo + 1)), "next"));
                    retVal.Link.Add(new BundleLink(new Uri(String.Format("{0}_page={1}", baseUri, nPages + 1)), "last"));
                }
            }
            else
            {
                retVal.Link.Add(new BundleLink(new Uri(baseUri), "self"));
            }

            // Updated
            retVal.Timestamp = DateTime.Now;
            //retVal.Generator = "MARC-HI Service Core Framework";

            // HACK: Remove me
            if (queryResult != null)
            {
                retVal.Total = queryResult.TotalResults;
            }

            //retVal.
            // Results
            if (result.Results != null)
            {
                var feedItems = new List <BundleEntry>();
                foreach (DomainResourceBase itm in result.Results)
                {
                    Uri         resourceUrl = new Uri(String.Format("{0}/{1}?_format={2}", WebOperationContext.Current.IncomingRequest.UriTemplateMatch.BaseUri, String.Format("{0}/{1}/_history/{2}", itm.GetType().Name, itm.Id, itm.VersionId), format));
                    BundleEntry feedResult  = new BundleEntry(); //new Bundleentry(String.Format("{0} id {1} version {2}", itm.GetType().Name, itm.Id, itm.VersionId), null ,resourceUrl);

                    feedResult.FullUrl = resourceUrl;

                    string summary = "<div xmlns=\"http://www.w3.org/1999/xhtml\">" + itm.Text.ToString() + "</div>";

                    // Add confidence if the attribute permits
                    ConfidenceAttribute confidence = itm.Attributes.Find(a => a is ConfidenceAttribute) as ConfidenceAttribute;
                    if (confidence != null)
                    {
                        feedResult.Search = new BundleSearch()
                        {
                            Score = confidence.Confidence
                        }
                    }
                    ;

                    feedResult.Resource = new BundleResrouce(itm);
                    feedItems.Add(feedResult);
                }
                retVal.Entry = feedItems;
            }

            // Outcome
            //if (result.Details.Count > 0 || result.Issues != null && result.Issues.Count > 0)
            //{
            //    var outcome = CreateOutcomeResource(result);
            //    retVal.ElementExtensions.Add(outcome, new XmlSerializer(typeof(OperationOutcome)));
            //    retVal.Description = new TextSyndicationContent(outcome.Text.ToString(), TextSyndicationContentKind.Html);
            //}
            return(retVal);
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Query the data store
        /// </summary>
        public static FhirQueryResult Query(ClientRegistryFhirQuery querySpec, List <IResultDetail> details)
        {
            // Get the services
            IClientRegistryDataService dataService  = ApplicationContext.CurrentContext.GetService(typeof(IClientRegistryDataService)) as IClientRegistryDataService;
            IQueryPersistenceService   queryService = ApplicationContext.CurrentContext.GetService(typeof(IQueryPersistenceService)) as IQueryPersistenceService;

            try
            {
                if (querySpec.Quantity > 100)
                {
                    throw new ConstraintException("Query limit must not exceed 100");
                }

                if (dataService == null)
                {
                    throw new InvalidOperationException("No persistence service has been configured, queries cannot continue without this service");
                }

                FhirQueryResult result = new FhirQueryResult();
                result.Query   = querySpec;
                result.Issues  = new List <DetectedIssue>();
                result.Details = details;
                result.Results = new List <SVC.Messaging.FHIR.Resources.ResourceBase>(querySpec.Quantity);

                VersionedDomainIdentifier[] identifiers;

                RegistryQueryRequest queryRequest = new RegistryQueryRequest()
                {
                    QueryRequest = querySpec.Filter,
                    QueryId      = querySpec.QueryId != Guid.Empty ? querySpec.QueryId.ToString() : null,
                    QueryTag     = querySpec.QueryId != Guid.Empty ? querySpec.QueryId.ToString(): null,
                    IsSummary    = !querySpec.IncludeHistory,
                    Offset       = querySpec.Start,
                    Limit        = querySpec.Quantity
                };


                // Is this a continue?
                queryRequest.IsContinue = (!String.IsNullOrEmpty(queryRequest.QueryId) && queryRequest.Offset > 0);

                var dataResults = dataService.Query(queryRequest);
                details.AddRange(dataResults.Details);

                result.TotalResults = dataResults.TotalResults;

                // Fetch the results
                foreach (HealthServiceRecordContainer res in dataResults.Results)
                {
                    if (res == null)
                    {
                        continue;
                    }

                    var resultSubject = res.FindComponent(HealthServiceRecordSiteRoleType.SubjectOf) as HealthServiceRecordContainer ?? res;
                    var processor     = FhirMessageProcessorUtil.GetComponentProcessor(resultSubject.GetType());
                    if (processor == null)
                    {
                        result.Details.Add(new NotImplementedResultDetail(ResultDetailType.Error, String.Format("Will not include {1}^^^&{2}&ISO in result set, cannot find converter for {0}", resultSubject.GetType().Name, resultSubject.Id, ApplicationContext.ConfigurationService.OidRegistrar.GetOid("CR_CID").Oid), null, null));
                    }
                    else
                    {
                        result.Results.Add(processor.ProcessComponent(resultSubject, details));
                    }
                }

                // Sort control?
                // TODO: Support sort control but for now just sort according to confidence then date
                //retVal.Sort((a, b) => b.Id.CompareTo(a.Id)); // Default sort by id

                //if (queryPersistence != null)
                //    result.TotalResults = (int)queryPersistence.QueryResultTotalQuantity(querySpec.QueryId.ToString());
                //else
                //    result.TotalResults = retRecordId.Count(o => o != null);

                return(result);
            }
            catch (Exception ex)
            {
                Trace.TraceError(ex.ToString());
                details.Add(new PersistenceResultDetail(ResultDetailType.Error, ex.Message, ex));
                throw;
            }
        }
Ejemplo n.º 8
0
 /// <summary>
 /// Process includes for the specified result set
 /// </summary>
 protected virtual void ProcessIncludes(IEnumerable <TModel> results, System.Collections.Specialized.NameValueCollection parameters, FhirQueryResult queryResult)
 {
     // Include or ref include?
     if (parameters["_include"] != null) // TODO: _include:iterate (fhir is crazy)
     {
         queryResult.Results = queryResult.Results.Union(results.SelectMany(h => this.GetIncludes(h, parameters["_include"].Split(',').Select(o => new IncludeInstruction(o)))).Select(o => new Bundle.EntryComponent()
         {
             Resource = o,
             Search   = new Bundle.SearchComponent()
             {
                 Mode = Bundle.SearchEntryMode.Include
             }
         })).ToList();
     }
     if (parameters["_revinclude"] != null) // TODO: _revinclude:iterate (fhir is crazy)
     {
         queryResult.Results = queryResult.Results.Union(results.SelectMany(h => this.GetReverseIncludes(h, parameters["_revinclude"].Split(',').Select(o => new IncludeInstruction(o)))).Select(o => new Bundle.EntryComponent()
         {
             Resource = o,
             Search   = new Bundle.SearchComponent()
             {
                 Mode = Bundle.SearchEntryMode.Include
             }
         })).ToList();
     }
 }
Ejemplo n.º 9
0
        /// <summary>
        /// Throw an appropriate exception based on the caught exception
        /// </summary>
        private object ErrorHelper(Exception e, FhirOperationResult result, bool returnBundle)
        {
            if (result == null && returnBundle)
            {
                result = new FhirQueryResult()
                {
                    Details = new List <IResultDetail>(), Query = new FhirQuery()
                    {
                        Start = 0, Quantity = 0
                    }
                }
            }
            ;
            else if (result == null)
            {
                result = new FhirOperationResult()
                {
                    Details = new List <IResultDetail>()
                    {
                        new ResultDetail(ResultDetailType.Error, "No information available", e)
                    }
                }
            }
            ;


            this.m_tracer.TraceEvent(TraceEventType.Error, 0, e.ToString());
            result.Details.Add(new ResultDetail(ResultDetailType.Error, e.Message, e));

            HttpStatusCode retCode = HttpStatusCode.OK;

            if (e is NotSupportedException)
            {
                retCode = System.Net.HttpStatusCode.MethodNotAllowed;
            }
            else if (e is NotImplementedException)
            {
                retCode = System.Net.HttpStatusCode.NotImplemented;
            }
            else if (e is InvalidDataException)
            {
                retCode = HttpStatusCode.BadRequest;
            }
            else if (e is FileLoadException)
            {
                retCode = System.Net.HttpStatusCode.Gone;
            }
            else if (e is FileNotFoundException || e is ArgumentException)
            {
                retCode = System.Net.HttpStatusCode.NotFound;
            }
            else if (e is ConstraintException)
            {
                retCode = (HttpStatusCode)422;
            }
            else
            {
                retCode = System.Net.HttpStatusCode.InternalServerError;
            }

            WebOperationContext.Current.OutgoingResponse.StatusCode = retCode;
            WebOperationContext.Current.OutgoingResponse.Format     = WebMessageFormat.Xml;

            if (returnBundle)
            {
                throw new WebFaultException <Bundle>(MessageUtil.CreateBundle(result), retCode);
            }
            else
            {
                WebOperationContext.Current.OutgoingResponse.Headers.Add("Content-Disposition", "filename=\"error.xml\"");
                throw e;
            }
            //return MessageUtil.CreateOutcomeResource(result);
        }
Ejemplo n.º 10
0
        /// <summary>
        /// Create a feed
        /// </summary>
        public static Bundle CreateBundle(FhirQueryResult result, Bundle.BundleType bundleType)
        {
            Bundle          retVal      = new Bundle();
            FhirQueryResult queryResult = result as FhirQueryResult;

            retVal.Id   = String.Format("urn:uuid:{0}", Guid.NewGuid());
            retVal.Type = bundleType;

            // Make the Self uri
            String baseUri = $"{result.ResourceType}";

            if (queryResult.Query != null)
            {
                int pageNo = queryResult == null || queryResult.Query.Quantity == 0 ? 0 : queryResult.Query.Start / queryResult.Query.Quantity,
                    nPages = queryResult == null || queryResult.Query.Quantity == 0 ? 1 : (queryResult.TotalResults / queryResult.Query.Quantity);
                retVal.Type = Bundle.BundleType.Searchset;
                var queryUri = baseUri + "?";

                // Self uri
                if (queryResult != null)
                {
                    for (int i = 0; i < queryResult.Query.ActualParameters.Count; i++)
                    {
                        foreach (var itm in queryResult.Query.ActualParameters.GetValues(i))
                        {
                            switch (queryResult.Query.ActualParameters.GetKey(i))
                            {
                            case "_stateid":
                            case "_page":
                            case "_count":
                                break;

                            default:
                                queryUri += string.Format("{0}={1}&", queryResult.Query.ActualParameters.GetKey(i), itm);
                                break;
                            }
                        }
                    }

                    if (!baseUri.Contains("_stateid=") && queryResult.Query.QueryId != Guid.Empty)
                    {
                        queryUri += String.Format("_stateid={0}&", queryResult.Query.QueryId);
                    }
                }


                // Self URI
                if (queryResult != null && queryResult.TotalResults > queryResult.Results.Count)
                {
                    retVal.Link.Add(new Bundle.LinkComponent()
                    {
                        Url = $"{queryUri}_page={pageNo}&_count={queryResult?.Query.Quantity ?? 100}", Relation = "self"
                    });
                    if (pageNo > 0)
                    {
                        retVal.Link.Add(new Bundle.LinkComponent()
                        {
                            Url = $"{queryUri}_page=0&_count={queryResult?.Query.Quantity ?? 100}", Relation = "first"
                        });
                        retVal.Link.Add(new Bundle.LinkComponent()
                        {
                            Url = $"{queryUri}_page={pageNo - 1}&_count={queryResult?.Query.Quantity ?? 100}", Relation = "previous"
                        });
                    }
                    if (pageNo <= nPages)
                    {
                        retVal.Link.Add(new Bundle.LinkComponent()
                        {
                            Url = $"{queryUri}_page={pageNo + 1}&_count={queryResult?.Query.Quantity ?? 100}", Relation = "next"
                        });
                        retVal.Link.Add(new Bundle.LinkComponent()
                        {
                            Url = $"{queryUri}_page={nPages}&_count={queryResult?.Query.Quantity ?? 100}", Relation = "last"
                        });
                    }
                }
                else
                {
                    retVal.Link.Add(new Bundle.LinkComponent()
                    {
                        Url = queryUri, Relation = "self"
                    });
                }
            }
            else //History
            {
                // History type
                retVal.Type = Bundle.BundleType.History;
                // Self URI
                retVal.Link.Add(new Bundle.LinkComponent()
                {
                    Url = $"{baseUri}/_history", Relation = "self"
                });
            }
            // Updated
            retVal.Timestamp = DateTime.Now;
            //retVal.Generator = "MARC-HI Service Core Framework";

            // HACK: Remove me
            if (queryResult != null)
            {
                retVal.Total = queryResult.TotalResults;
            }


            // Results
            if (result.Results != null)
            {
                retVal.Entry = result.Results.Select(itm =>
                {
                    itm.Link = new List <Bundle.LinkComponent>()
                    {
                        new Bundle.LinkComponent()
                        {
                            Relation = "_self", Url = itm.Resource.HasVersionId ? $"{itm.Resource.TypeName}/{itm.Resource.Id}/_history/{itm.Resource.VersionId}" : $"{itm.Resource.TypeName}/{itm.Resource.Id}"
                        }
                    };
                    itm.FullUrl = itm.FullUrl ?? $"{GetBaseUri()}/{itm.Resource.TypeName}/{itm.Resource.Id}";

                    // Add confidence if the attribute permits
                    if (itm.Search != null && itm.Search.Mode == Bundle.SearchEntryMode.Match) // Search data
                    {
                        var confidence = itm.Annotations(typeof(ITag)).OfType <ITag>().FirstOrDefault(t => t.TagKey == "$conf");
                        if (confidence != null)
                        {
                            itm.Search.Score = Decimal.Parse(confidence.Value);
                        }
                    }

                    return(itm);
                }).ToList();
            }

            // Outcome
            //if (result.Details.Count > 0 || result.Issues != null && result.Issues.Count > 0)
            //{
            //    var outcome = CreateOutcomeResource(result);
            //    retVal.ElementExtensions.Add(outcome, XmlModelSerializerFactory.Current.CreateSerializer(typeof(OperationOutcome)));
            //    retVal.Description = new TextSyndicationContent(outcome.Text.ToString(), TextSyndicationContentKind.Html);
            //}
            return(retVal);
        }