コード例 #1
0
        /// <summary>
        /// This method validates if transaction bundle contains multiple entries that are modifying the same resource.
        /// It also validates if the request operations within a entry is a valid operation.
        /// </summary>
        /// <param name="bundle"> The input bundle</param>
        /// <param name="cancellationToken"> The cancellation token</param>
        public async Task ValidateBundle(Hl7.Fhir.Model.Bundle bundle, CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNull(bundle, nameof(bundle));

            var resourceIdList = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

            foreach (var entry in bundle.Entry)
            {
                if (ShouldValidateBundleEntry(entry))
                {
                    string resourceId = await GetResourceId(entry, cancellationToken);

                    string conditionalCreateQuery = entry.Request.IfNoneExist;

                    if (!string.IsNullOrEmpty(resourceId))
                    {
                        // Throw exception if resourceId is already present in the hashset.
                        if (resourceIdList.Contains(resourceId))
                        {
                            string requestUrl = BuildRequestUrlForConditionalQueries(entry, conditionalCreateQuery);
                            throw new RequestNotValidException(string.Format(Api.Resources.ResourcesMustBeUnique, requestUrl));
                        }

                        resourceIdList.Add(resourceId);
                    }
                }
            }
        }
コード例 #2
0
        public override async Task <FhirHealthCheckStatus> CheckHealth(CancellationToken token = default)
        {
            try
            {
                while (!token.IsCancellationRequested)
                {
                    SearchParams          search = new SearchParams().SetCount(1);
                    Hl7.Fhir.Model.Bundle result = await _client.SearchAsync <Hl7.Fhir.Model.StructureDefinition>(search);

                    return(await Task.FromResult(new FhirHealthCheckStatus(string.Empty, 200)));
                }

                token.ThrowIfCancellationRequested();
                return(await Task.FromResult(new FhirHealthCheckStatus(token.ToString(), 500)));
            }
            catch (FhirOperationException ex)
            {
                return(await Task.FromResult(new FhirHealthCheckStatus(ex.Message, (int)ex.Status)));
            }
            catch (IdentityModel.Clients.ActiveDirectory.AdalServiceException ex)
            {
                return(await Task.FromResult(new FhirHealthCheckStatus(ex.Message, ex.StatusCode)));
            }
#pragma warning disable CA1031
            catch (Exception ex)
#pragma warning restore CA1031
            {
                return(await Task.FromResult(new FhirHealthCheckStatus(ex.Message, 500)));
            }
        }
コード例 #3
0
ファイル: BundleHandler.cs プロジェクト: stvb/fhir-server
 private async Task ExecuteAllRequests(Hl7.Fhir.Model.Bundle responseBundle)
 {
     await ExecuteRequests(responseBundle, Hl7.Fhir.Model.Bundle.HTTPVerb.DELETE);
     await ExecuteRequests(responseBundle, Hl7.Fhir.Model.Bundle.HTTPVerb.POST);
     await ExecuteRequests(responseBundle, Hl7.Fhir.Model.Bundle.HTTPVerb.PUT);
     await ExecuteRequests(responseBundle, Hl7.Fhir.Model.Bundle.HTTPVerb.GET);
 }
コード例 #4
0
        /// <inheritdoc/>
        public async Task <RequestResult <IEnumerable <ImmunizationView> > > GetImmunizations(string hdid)
        {
            this.logger.LogDebug($"Getting immunization from Immunization Service... {hdid}");

            RequestResult <IEnumerable <ImmunizationView> > result = new RequestResult <IEnumerable <ImmunizationView> >();
            string jwtString = this.httpContextAccessor.HttpContext.Request.Headers["Authorization"][0];
            RequestResult <Patient> patientResult = this.patientDelegate.GetPatient(hdid, jwtString);

            if (patientResult.ResultStatus == ResultType.Success && patientResult.ResourcePayload != null)
            {
                ImmunizationRequest request = new ImmunizationRequest()
                {
                    PersonalHealthNumber = patientResult.ResourcePayload.PersonalHealthNumber,
                    DateOfBirth          = patientResult.ResourcePayload.Birthdate,
                };

                Hl7.Fhir.Model.Bundle fhirBundle = await this.immunizationDelegate.GetImmunizationBundle(request).ConfigureAwait(true);

                IEnumerable <Hl7.Fhir.Model.Immunization> immmsLiist = fhirBundle.Entry
                                                                       .Where(r => r.Resource is Hl7.Fhir.Model.Immunization)
                                                                       .Select(f => (Hl7.Fhir.Model.Immunization)f.Resource);

                List <ImmunizationView> immunizations = new List <ImmunizationView>();
                foreach (Hl7.Fhir.Model.Immunization entry in immmsLiist)
                {
                    ImmunizationView immunizationView = new ImmunizationView
                    {
                        Id                 = entry.Id,
                        Name               = entry.VaccineCode.Text,
                        Status             = entry.Status.ToString() !,
                        OccurrenceDateTime = System.DateTime.Parse(entry.Occurrence.ToString() !, CultureInfo.InvariantCulture),
                    };
                    foreach (Hl7.Fhir.Model.Coding code in entry.VaccineCode.Coding)
                    {
                        ImmunizationAgent immunizationAgent = new ImmunizationAgent
                        {
                            Code = code.Code,
                            Name = code.Display,
                        };
                        immunizationView.ImmunizationAgents.Add(immunizationAgent);
                    }

                    immunizations.Add(immunizationView);
                }
                result.ResultStatus     = ResultType.Success;
                result.PageIndex        = 0;
                result.PageSize         = immunizations.Count;
                result.TotalResultCount = immunizations.Count;
                result.ResourcePayload  = immunizations;
            }
            else
            {
                result.ResultError = patientResult.ResultError;
            }


            this.logger.LogDebug($"Finished getting immunization records {result.TotalResultCount}");
            return(result);
        }
コード例 #5
0
        static void Main(string[] args)
        {
            //The fhir server end point address
            string ServiceRootUrl = "https://api-v5-stu3.hspconsortium.org/MX1STU3/open";
            //string ServiceRootUrl = "http://sqlonfhir-stu3.azurewebsites.net/fhir";
            //Create a client to send to the server at a given endpoint.
            var FhirClient = new Hl7.Fhir.Rest.FhirClient(ServiceRootUrl);

            // increase timeouts since the server might be powered down
            FhirClient.Timeout = (60 * 1000);

            Console.WriteLine("Press any key to send to server: " + ServiceRootUrl);
            Console.WriteLine();
            Console.ReadKey();
            try
            {
                //Attempt to send the resource to the server endpoint
                UriBuilder UriBuilderx = new UriBuilder(ServiceRootUrl);
                UriBuilderx.Path = "Patient/MY_PATIENT_ID";
                Hl7.Fhir.Model.Resource ReturnedResource = FhirClient.InstanceOperation(UriBuilderx.Uri, "everything");

                if (ReturnedResource is Hl7.Fhir.Model.Bundle)
                {
                    Hl7.Fhir.Model.Bundle ReturnedBundle = ReturnedResource as Hl7.Fhir.Model.Bundle;
                    Console.WriteLine("Received: " + ReturnedBundle.Total + " results, the resources are: ");
                    foreach (var Entry in ReturnedBundle.Entry)
                    {
                        Console.WriteLine(string.Format("{0}/{1}", Entry.Resource.TypeName, Entry.Resource.Id));
                    }
                }
                else
                {
                    throw new Exception("Operation call must return a bundle resource");
                }
                Console.WriteLine();
            }
            catch (Hl7.Fhir.Rest.FhirOperationException FhirOpExec)
            {
                //Process any Fhir Errors returned as OperationOutcome resource
                Console.WriteLine();
                Console.WriteLine("An error message: " + FhirOpExec.Message);
                Console.WriteLine();
                string    xml  = Hl7.Fhir.Serialization.FhirSerializer.SerializeResourceToXml(FhirOpExec.Outcome);
                XDocument xDoc = XDocument.Parse(xml);
                Console.WriteLine(xDoc.ToString());
            }
            catch (Exception GeneralException)
            {
                Console.WriteLine();
                Console.WriteLine("An error message: " + GeneralException.Message);
                Console.WriteLine();
            }
            Console.WriteLine("Press any key to end.");
            Console.ReadKey();
        }
コード例 #6
0
 private Hl7.Fhir.Model.Bundle GetFhirSubBundle(Hl7.Fhir.Model.Bundle bundle)
 {
     foreach (Hl7.Fhir.Model.Bundle.EntryComponent ec in bundle.Entry)
     {
         if (ec?.Resource is Hl7.Fhir.Model.Bundle)
         {
             return(ec.Resource as Hl7.Fhir.Model.Bundle);
         }
     }
     return(null);
 }
コード例 #7
0
        /// <summary>Writes a saner.</summary>
        /// <param name="outputDirectory">Directory to use for export files.</param>
        /// <param name="data">           The data.</param>
        private static void WriteSanerJson(
            string outputDirectory,
            ReportData data)
        {
            Hl7.Fhir.Model.Bundle bundle = SanerMeasureReport.GetBundle(data, true, true);

            string filename = Path.Combine(outputDirectory, $"{data.Reporter.Name}-saner.json");

            File.WriteAllText(
                filename,
                _jsonSerializer.SerializeToString(bundle));
        }
コード例 #8
0
 private Immunization GetImmunization(Hl7.Fhir.Model.Bundle bundle)
 {
     Hl7.Fhir.Model.Immunization fhirImmunization = GetFhirImmunization(bundle);
     Hl7.Fhir.Model.Patient      fhirPatient      = GetFhirPatient(bundle);
     return(new Immunization
     {
         RecordIdentifier = fhirImmunization.Id,
         Patient = GetPatient(fhirPatient),
         LotNumber = fhirImmunization.LotNumber,
         DoseNumber = GetDoseNumber(fhirImmunization),
         CountryOfVaccination = "CA",
         VaccinationDate = DateTime.Parse(fhirImmunization.Date).ToString("yyyy-MM-dd")
     });
 }
コード例 #9
0
ファイル: Program.cs プロジェクト: docdevore/SimpleFHIR
        static void Main(string[] args)
        {
            //The fhir server end point address
            //string ServiceRootUrl = "https://api-v5-stu3.hspconsortium.org/MX1STU3/open";
            string ServiceRootUrl = "http://PyroHealth.net/test/stu3/fhir";
            //Create a client to send to the server at a given endpoint.
            var FhirClient = new Hl7.Fhir.Rest.FhirClient(ServiceRootUrl);

            // increase timeouts since the server might be powered down
            FhirClient.Timeout = (60 * 1000);

            Console.WriteLine("Press any key to send to server: " + ServiceRootUrl);
            Console.WriteLine();
            Console.ReadKey();
            try
            {
                //Attempt to send the resource to the server endpoint
                Hl7.Fhir.Model.Bundle ReturnedSearchBundle = FhirClient.Search <Hl7.Fhir.Model.Patient>(new string[] { "family=Fhirman" });
                Console.WriteLine(string.Format("Found: {0} Fhirman patients.", ReturnedSearchBundle.Total.ToString()));
                Console.WriteLine("Their logical IDs are:");
                foreach (var Entry in ReturnedSearchBundle.Entry)
                {
                    Console.WriteLine("ID: " + Entry.Resource.Id);
                }
                Console.WriteLine();
            }
            catch (Hl7.Fhir.Rest.FhirOperationException FhirOpExec)
            {
                //Process any Fhir Errors returned as OperationOutcome resource
                Console.WriteLine();
                Console.WriteLine("An error message: " + FhirOpExec.Message);
                Console.WriteLine();
                string    xml  = Hl7.Fhir.Serialization.FhirSerializer.SerializeResourceToXml(FhirOpExec.Outcome);
                XDocument xDoc = XDocument.Parse(xml);
                Console.WriteLine(xDoc.ToString());
            }
            catch (Exception GeneralException)
            {
                Console.WriteLine();
                Console.WriteLine("An error message: " + GeneralException.Message);
                Console.WriteLine();
            }
            Console.WriteLine("Press any key to end.");
            Console.ReadKey();
        }
コード例 #10
0
        private (Hl7.Fhir.Model.Bundle rawBundle, Hl7.Fhir.Model.Bundle bundle) CreateBundle(params ResourceElement[] resources)
        {
            string id        = Guid.NewGuid().ToString();
            var    rawBundle = new Hl7.Fhir.Model.Bundle();
            var    bundle    = new Hl7.Fhir.Model.Bundle();

            rawBundle.Id    = bundle.Id = id;
            rawBundle.Type  = bundle.Type = BundleType.Searchset;
            rawBundle.Entry = new List <EntryComponent>();
            rawBundle.Total = resources.Count();
            bundle.Entry    = new List <EntryComponent>();
            bundle.Total    = resources.Count();

            foreach (var resource in resources)
            {
                var poco = resource.ToPoco();
                poco.VersionId        = "1";
                poco.Meta.LastUpdated = Clock.UtcNow;
                poco.Meta.Tag         = new List <Hl7.Fhir.Model.Coding>
                {
                    new Hl7.Fhir.Model.Coding {
                        System = "testTag", Code = Guid.NewGuid().ToString()
                    },
                };
                var wrapper = _wrapperFactory.Create(poco.ToResourceElement(), deleted: false, keepMeta: true);
                wrapper.Version = "1";

                var requestComponent = new RequestComponent {
                    Method = HTTPVerb.POST, Url = "patient/"
                };
                var responseComponent = new ResponseComponent {
                    Etag = "W/\"1\"", LastModified = DateTimeOffset.UtcNow, Status = "201 Created"
                };
                rawBundle.Entry.Add(new RawBundleEntryComponent(wrapper)
                {
                    Request  = requestComponent,
                    Response = responseComponent,
                });
                bundle.Entry.Add(new EntryComponent {
                    Resource = poco, Request = requestComponent, Response = responseComponent
                });
            }

            return(rawBundle, bundle);
        }
コード例 #11
0
 private Hl7.Fhir.Model.Immunization GetFhirImmunization(Hl7.Fhir.Model.Bundle bundle)
 {
     foreach (Hl7.Fhir.Model.Bundle.EntryComponent ec in bundle.Entry)
     {
         if (ec != null && ec.Resource is Hl7.Fhir.Model.Bundle)
         {
             Hl7.Fhir.Model.Bundle subBundle = ec.Resource as Hl7.Fhir.Model.Bundle;
             foreach (Hl7.Fhir.Model.Bundle.EntryComponent ec2 in subBundle.Entry)
             {
                 if (ec2?.Resource is Hl7.Fhir.Model.Immunization)
                 {
                     return(ec2.Resource as Hl7.Fhir.Model.Immunization);
                 }
             }
         }
     }
     return(null);
 }
コード例 #12
0
        public List <Patient> SearchPatients(string searchString)
        {
            var patientResults = new List <Patient>();


            Hl7.Fhir.Rest.FhirClient fhirClient = FhirClientManager.CreateClientConnection(Configuration);

            fhirClient = FhirClientManager.CreateClientConnection(Configuration);

            Hl7.Fhir.Model.Bundle ReturnedSearchBundle =
                fhirClient.Search <Hl7.Fhir.Model.Patient>(new string[] { $"name={searchString}" });

            foreach (var resource in ReturnedSearchBundle.Entry)
            {
                patientResults.Add((Patient)resource.Resource);
            }

            return(patientResults);
        }
コード例 #13
0
        public List <Prescription> FindMedication(string patientId)
        {
            var prescriptionResults = new List <Prescription>();


            Hl7.Fhir.Rest.FhirClient fhirClient = FhirClientManager.CreateClientConnection(Configuration);

            fhirClient = FhirClientManager.CreateClientConnection(Configuration);

            Hl7.Fhir.Model.Bundle ReturnedSearchBundle =
                fhirClient.Search <Hl7.Fhir.Model.MedicationRequest>(new string[] { $"patient={patientId}" });

            foreach (var resource in ReturnedSearchBundle.Entry)
            {
                var prescription = (Prescription)resource.Resource;
                prescriptionResults.Add(prescription);
            }

            return(prescriptionResults);
        }
コード例 #14
0
        private async Task Validate(Hl7.Fhir.Model.Bundle rawBundle, Hl7.Fhir.Model.Bundle bundle)
        {
            string serialized;

            using (var ms = new MemoryStream())
                using (var sr = new StreamReader(ms))
                {
                    await _bundleSerializer.Serialize(rawBundle, ms);

                    ms.Seek(0, SeekOrigin.Begin);
                    serialized = await sr.ReadToEndAsync();
                }

            string originalSerializer = bundle.ToJson();

            Assert.Equal(originalSerializer, serialized);

            var deserializedBundle = new FhirJsonParser(DefaultParserSettings.Settings).Parse(serialized) as Hl7.Fhir.Model.Bundle;

            Assert.True(deserializedBundle.IsExactly(bundle));
        }
コード例 #15
0
        public async Task <IEnumerable <Immunization> > GetImmunizations(string patientId, string accessToken = null)
        {
            IList <Immunization> immunizations = new List <Immunization>();

            MedicalLogResponse response = await _medicalLogClient.GetMedicalLogAsync(new Guid(patientId));

            FhirJsonParser fjp = new();

            Hl7.Fhir.Model.Bundle bundle = fjp.Parse <Hl7.Fhir.Model.Bundle>(response.FhirBundle);
            if (bundle != null)
            {
                Hl7.Fhir.Model.Patient fhirPatient = GetFhirPatient(bundle);
                Hl7.Fhir.Model.Bundle  subBundle   = GetFhirSubBundle(bundle);
                if (subBundle != null)
                {
                    foreach (Hl7.Fhir.Model.Bundle.EntryComponent ec in subBundle.Entry)
                    {
                        if (ec?.Resource is Hl7.Fhir.Model.Immunization)
                        {
                            Hl7.Fhir.Model.Immunization fhirImmunization = ec.Resource as Hl7.Fhir.Model.Immunization;
                            Immunization immunization = new Immunization
                            {
                                RecordIdentifier     = patientId + "_" + fhirImmunization.Id,
                                Patient              = GetPatient(fhirPatient),
                                LotNumber            = fhirImmunization.LotNumber,
                                DoseNumber           = GetDoseNumber(fhirImmunization),
                                CountryOfVaccination = "CA",
                                VaccinationDate      = DateTime.Parse(fhirImmunization.Date).ToString("yyyy-MM-dd"),
                                Vaccine              = GetVaccine(),
                                Facility             = fhirImmunization.Location.Display
                            };
                            immunizations.Add(immunization);
                        }
                    }
                }
            }
            return(immunizations);
        }
コード例 #16
0
        /// <summary>
        /// Validates if transaction bundle contains multiple entries that are modifying the same resource.
        /// </summary>
        /// <param name="bundle"> The input bundle</param>
        public static void ValidateTransactionBundle(Hl7.Fhir.Model.Bundle bundle)
        {
            var resourceIdList = new HashSet <string>(StringComparer.Ordinal);

            foreach (var entry in bundle.Entry)
            {
                if (ShouldValidateBundleEntry(entry))
                {
                    string resourceId = GetResourceUrl(entry);

                    if (!string.IsNullOrEmpty(resourceId))
                    {
                        // Throw exception if resourceId is already present in the hashset.
                        if (resourceIdList.Contains(resourceId))
                        {
                            throw new RequestNotValidException(string.Format(Api.Resources.ResourcesMustBeUnique, resourceId));
                        }

                        resourceIdList.Add(resourceId);
                    }
                }
            }
        }
コード例 #17
0
        private void GetPatientsFromBundle(Hl7.Fhir.Model.Bundle bundle)
        {
            Bundle = bundle;

            CanMoveFirst    = Bundle.FirstLink == null;
            CanMovePrevious = Bundle.PreviousLink == null;
            CanMoveNext     = Bundle.NextLink == null;
            CanMoveLast     = Bundle.LastLink == null;

            if (Patients.Count > 0)
            {
                Patients.Clear();
            }
            foreach (var e in Bundle.Entry)
            {
                var patient = new LockStepPatient();
                Hl7.Fhir.Model.Patient p = (Hl7.Fhir.Model.Patient)e.Resource;
                //var meds = PatientService.GetMedicationRequestsAsync(p.Id).GetAwaiter().GetResult();
                patient.FhirPatient = p;
                // patient.Medications = meds.Requests;
                Patients.Add(patient);
            }
        }
コード例 #18
0
ファイル: Program.cs プロジェクト: hmandirola/Open-RSD
        static Hl7.Fhir.Model.Bundle GetPatient()
        {
            var baseUrl = "https://testapp.hospitalitaliano.org.ar/masterfile-federacion-service/fhir";

            var client = new Hl7.Fhir.Rest.FhirClient(baseUrl);

            client.OnBeforeRequest += OnBeforeRequestFhirServer;
            client.OnAfterResponse += OnAfterResponseFhirServer;

            Hl7.Fhir.Model.Bundle ret = null;

            try
            {
                var qp = new Hl7.Fhir.Rest.SearchParams();
                qp.Add("identifier", "http://www.msal.gov.ar|2222222");
                ret = client.Search <Hl7.Fhir.Model.Patient>(qp);
            }
            catch (Exception ex)
            {
                Log.Error(ex.ToString());
            }

            return(ret);
        }
コード例 #19
0
 /// <summary>
 /// This method validates if transaction bundle contains multiple entries that are modifying the same resource.
 /// It also validates if the request operations within a entry is a valid operation.
 /// If a conditional create or update is executed and a resource exists, the value is populated in the idDictionary.
 /// </summary>
 /// <param name="bundle"> The input bundle</param>
 /// <param name="idDictionary">The id dictionary that stores fullUrl to actual ids.</param>
 /// <param name="cancellationToken"> The cancellation token</param>
 public async Task ValidateBundle(Hl7.Fhir.Model.Bundle bundle, IDictionary <string, (string resourceId, string resourceType)> idDictionary, CancellationToken cancellationToken)
コード例 #20
0
ファイル: PatientManager.cs プロジェクト: hmandirola/Open-RSD
        public Hl7.Fhir.Model.Bundle GetPatientByIdentifier(string system, string value)
        {
            if (string.IsNullOrWhiteSpace(system))
            {
                throw new ArgumentException("message", nameof(system));
            }

            if (string.IsNullOrWhiteSpace(value))
            {
                throw new ArgumentException("message", nameof(value));
            }

            var activity = new ActivityLog
            {
                ActivityTypeDescriptorId = (int)Entities.Activity.ActivityType.GET_PACIENTE_EN_BUS_BY_IDENTIFIER
            };


            //var baseUrl = "https://testapp.hospitalitaliano.org.ar/masterfile-federacion-service/fhir";
            var baseUrl = _integrationServicesConfiguration.Services
                          .First(x => x.ServiceName == "BUS").BaseURL;

            var client = new FhirClient(baseUrl);

            client.PreferredFormat  = ResourceFormat.Json;
            client.OnBeforeRequest += (object sender, BeforeRequestEventArgs e) =>
            {
                var requestAdderss = e.RawRequest.Address.ToString();

                activity.RequestIsURL      = true;
                activity.ActivityRequestUI = requestAdderss;

                _logger.LogInformation($"Send Request Address:{requestAdderss}");
            };

            client.OnAfterResponse += (object sender, AfterResponseEventArgs e) =>
            {
                if (e.Body != null)
                {
                    var responseBody = Encoding.UTF8.GetString(e.Body, 0, e.Body.Length);

                    //Prettify !!!
                    responseBody = JToken.Parse(responseBody).ToString();

                    activity.ResponseIsJson       = true;
                    activity.ActivityResponse     = $"Status: {e.RawResponse.StatusCode}";
                    activity.ActivityResponseBody = responseBody;

                    _logger.LogInformation($"Received response with status: {e.RawResponse.StatusCode}");
                    _logger.LogInformation($"Received response with body: {responseBody}");
                }
            };


            Hl7.Fhir.Model.Bundle ret = null;

            try
            {
                var qp = new SearchParams();
                qp.Add("identifier", $"{system}|{value}");
                ret = client.Search <Hl7.Fhir.Model.Patient>(qp);
                _currentContext.RegisterActivityLog(activity);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex.ToString());
                throw ex;
            }

            return(ret);
        }
コード例 #21
0
        public async Task Serialize(Hl7.Fhir.Model.Bundle bundle, Stream outputStream, bool pretty = false)
        {
            await using Utf8JsonWriter writer     = new Utf8JsonWriter(outputStream, pretty ? _indentedWriterOptions : _writerOptions);
            await using StreamWriter streamWriter = new StreamWriter(outputStream, leaveOpen: true);

            writer.WriteStartObject();

            writer.WriteString("resourceType", bundle.TypeName);
            writer.WriteString("id", bundle.Id);

            SerializeMetadata();

            writer.WriteString("type", bundle.Type?.GetLiteral());

            SerializeLinks();

            if (bundle.Total.HasValue)
            {
                writer.WriteNumber("total", bundle.Total.Value);
            }

            await SerializeEntries();

            writer.WriteEndObject();
            await writer.FlushAsync();

            void SerializeMetadata()
            {
                if (bundle.Meta != null)
                {
                    writer.WriteStartObject("meta");
                    writer.WriteString("lastUpdated", bundle.Meta?.LastUpdated?.ToInstantString());
                    writer.WriteEndObject();
                }
            }

            void SerializeLinks()
            {
                if (bundle.Link?.Any() == true)
                {
                    writer.WriteStartArray("link");

                    foreach (var link in bundle.Link)
                    {
                        writer.WriteStartObject();
                        writer.WritePropertyName("relation");
                        writer.WriteStringValue(link.Relation);
                        writer.WritePropertyName("url");
                        writer.WriteStringValue(link.Url);
                        writer.WriteEndObject();
                    }

                    writer.WriteEndArray();
                }
            }

            async Task SerializeEntries()
            {
                if (bundle.Entry?.Any() == true)
                {
                    writer.WriteStartArray("entry");
                    foreach (var entry in bundle.Entry)
                    {
                        if (!(entry is RawBundleEntryComponent rawBundleEntry))
                        {
                            throw new ArgumentException("BundleSerializer can only be used when all Entry elements are of type RawBundleEntryComponent.", nameof(bundle));
                        }

                        bool wroteFullUrl = false;
                        writer.WriteStartObject();

                        if (!string.IsNullOrEmpty(rawBundleEntry.FullUrl))
                        {
                            writer.WriteString("fullUrl", rawBundleEntry.FullUrl);
                            await writer.FlushAsync();

                            await streamWriter.WriteAsync(",");

                            wroteFullUrl = true;
                        }

                        await writer.FlushAsync();

                        await streamWriter.WriteAsync("\"resource\":");

                        await streamWriter.FlushAsync();

                        await rawBundleEntry.ResourceElement.SerializeToStreamAsUtf8Json(outputStream);

                        if (!wroteFullUrl && (rawBundleEntry?.Search?.Mode != null || rawBundleEntry.Request != null || rawBundleEntry.Response != null))
                        {
                            // If fullUrl was written, the Utf8JsonWriter knows it needs to write a comma before the next property since a comma is needed, and will do so.
                            // If fullUrl wasn't written, since we are writing resource in a separate writer, we need to add this comma manually.
                            await streamWriter.WriteAsync(",");

                            await streamWriter.FlushAsync();
                        }

                        if (rawBundleEntry?.Search?.Mode != null)
                        {
                            writer.WriteStartObject("search");
                            writer.WriteString("mode", rawBundleEntry.Search?.Mode?.GetLiteral());
                            writer.WriteEndObject();
                        }

                        if (rawBundleEntry.Request != null)
                        {
                            writer.WriteStartObject("request");

                            writer.WriteString("method", rawBundleEntry.Request.Method.GetLiteral());
                            writer.WriteString("url", rawBundleEntry.Request.Url);

                            writer.WriteEndObject();
                        }

                        if (rawBundleEntry.Response != null)
                        {
                            writer.WriteStartObject("response");

                            writer.WriteString("status", rawBundleEntry.Response.Status);
                            writer.WriteString("etag", rawBundleEntry.Response.Etag);
                            writer.WriteString("lastModified", rawBundleEntry.Response.LastModified?.ToInstantString());

                            writer.WriteEndObject();
                        }

                        writer.WriteEndObject();
                    }

                    writer.WriteEndArray();
                }
            }
        }