/// <summary> /// Generate the FHIR Operation Parameter Resource for the HI Service operation call /// </summary> /// <returns></returns> private Parameters GenerateRequestParametersResource() { var ParametersResource = new Parameters(); ParametersResource.Parameter = new List <Parameters.ParameterComponent>(); var UserParam = new Parameters.ParameterComponent(); UserParam.Name = "UserId"; UserParam.Value = new FhirString("UserABC"); ParametersResource.Parameter.Add(UserParam); var UserIdQualifierParam = new Parameters.ParameterComponent(); UserIdQualifierParam.Name = "UserIdQualifier"; UserIdQualifierParam.Value = new FhirUri("http://ns.yourcompany.com.au/id/yoursoftware/userid/1.0"); ParametersResource.Parameter.Add(UserIdQualifierParam); var ReturnAuditEventParam = new Parameters.ParameterComponent(); ReturnAuditEventParam.Name = "ReturnAuditEvent"; ReturnAuditEventParam.Value = new FhirBoolean(true); ParametersResource.Parameter.Add(ReturnAuditEventParam); var RequestPatientParam = new Parameters.ParameterComponent(); RequestPatientParam.Name = "RequestPatient"; RequestPatientParam.Resource = GetRequestPatientResource(); ParametersResource.Parameter.Add(RequestPatientParam); return(ParametersResource); }
/// <summary> /// Deserialize JSON into a FHIR Parameters#Parameter /// </summary> public static void DeserializeJson(this Parameters.ParameterComponent current, ref Utf8JsonReader reader, JsonSerializerOptions options) { string propertyName; while (reader.Read()) { if (reader.TokenType == JsonTokenType.EndObject) { return; } if (reader.TokenType == JsonTokenType.PropertyName) { propertyName = reader.GetString(); if (Hl7.Fhir.Serialization.FhirSerializerOptions.Debug) { Console.WriteLine($"Parameters.ParameterComponent >>> Parameters#Parameter.{propertyName}, depth: {reader.CurrentDepth}, pos: {reader.BytesConsumed}"); } reader.Read(); current.DeserializeJsonProperty(ref reader, options, propertyName); } } throw new JsonException($"Parameters.ParameterComponent: invalid state! depth: {reader.CurrentDepth}, pos: {reader.BytesConsumed}"); }
private string paramValueToString(Parameters.ParameterComponent parameter) { if (parameter.Value != null) { switch (parameter.Value) { case Identifier id: return(id.ToToken()); case Coding coding: return(coding.ToToken()); case ContactPoint contactPoint: return(contactPoint.ToToken()); case CodeableConcept codeableConcept: return(codeableConcept.ToToken()); default: if (ModelInfo.IsPrimitive(parameter.Value.GetType())) { return(parameter.Value.ToString()); } break; } } throw Error.InvalidOperation($"Parameter '{parameter.Name}' has a non-primitive type, which is not allowed."); }
public static bool TryGetBooleanValue(this Parameters parameters, string name, out bool booleanValue) { Parameters.ParameterComponent param = parameters?.GetSingle(name); if (param == null) { booleanValue = false; return(false); } return(param.TryGetBooleanValue(out booleanValue)); }
private bool SetSearchParameterServerIndex(string Id) { string ResourceName = ResourceType.SearchParameter.GetLiteral(); Parameters Parameters = new Parameters(); Parameters.Parameter = new List <Parameters.ParameterComponent>(); var ParameterComponent = new Parameters.ParameterComponent(); Parameters.Parameter.Add(ParameterComponent); ParameterComponent.Name = $"{ResourceName}"; var Ref = new ResourceReference(); IRequestMeta RequestMetaTemp = IRequestMetaFactory.CreateRequestMeta().Set($"{ResourceName}/{Id}"); Ref.Reference = $"{RequestMetaTemp.PyroRequestUri.FhirRequestUri.OriginalString}"; ParameterComponent.Value = Ref; string OperationName = FhirOperationEnum.OperationType.ServerIndexesSet.GetPyroLiteral(); IRequestMeta RequestMeta = IRequestMetaFactory.CreateRequestMeta().Set(""); try { IFhirBaseOperationService FhirBaseOperationService = IFhirBaseOperationServiceFactory.CreateFhirBaseOperationService(); IResourceServiceOutcome ResourceServiceOutcome = IServerSearchParameterOperation.ProcessSet(RequestMeta.PyroRequestUri, RequestMeta.SearchParameterGeneric, Parameters, true); if (ResourceServiceOutcome.HttpStatusCode == System.Net.HttpStatusCode.OK) { return(true); } else { string OperationOutcomeMessage = string.Empty; if (ResourceServiceOutcome.ResourceResult != null && ResourceServiceOutcome.ResourceResult is OperationOutcome OptOut) { OptOut.Issue.ForEach(x => OperationOutcomeMessage = OperationOutcomeMessage + " " + x.Details.Text); string Message = $"Internal Server Error: Failed to Set SearchParameter Server index calling operation ${OperationName} with the SearchParmeter Resource {RequestMetaTemp.PyroRequestUri.FhirRequestUri.OriginalString} with extra detail: {OperationOutcomeMessage}"; ILog.Error(Message); throw new Exception(Message); } else { string Message = $"Internal Server Error: Failed to Set SearchParameter Server index calling operation ${OperationName} with the SearchParmeter Resource {RequestMetaTemp.PyroRequestUri.FhirRequestUri.OriginalString}"; ILog.Error(Message); throw new Exception(Message); } } } catch (Exception Exec) { string Message = $"{Exec.Message}, Internal Server Error: Failed to Set SearchParameter Server index calling operation ${OperationName}"; ILog.Error(Exec, Message); throw new Exception(Message); } }
public async Task GivenANewSearchParam_WhenReindexingComplete_ThenResourcesSearchedWithNewParamReturned() { var patientName = Guid.NewGuid().ToString().ComputeHash().Substring(28).ToLower(); var patient = new Patient { Name = new List <HumanName> { new HumanName { Family = patientName } } }; var searchParam = Samples.GetJsonSample <SearchParameter>("SearchParameter"); // POST a new patient FhirResponse <Patient> expectedPatient = await Client.CreateAsync(patient); // POST a second patient to show it is filtered and not returned when using the new search parameter await Client.CreateAsync(Samples.GetJsonSample <Patient>("Patient")); // POST a new Search parameter await Client.CreateAsync(searchParam); Uri reindexJobUri; try { // Start a reindex job (_, reindexJobUri) = await Client.PostReindexJobAsync(new Parameters()); } catch (FhirException ex) when(ex.StatusCode == HttpStatusCode.BadRequest && ex.Message.Contains("not enabled")) { Skip.If(!_fixture.IsUsingInProcTestServer, "Reindex is not enabled on this server."); return; } await WaitForReindexStatus(reindexJobUri, "Running", "Completed"); FhirResponse <Parameters> reindexJobResult = await Client.CheckReindexAsync(reindexJobUri); Parameters.ParameterComponent param = reindexJobResult.Resource.Parameter.FirstOrDefault(p => p.Name == "searchParams"); Assert.Contains("http://hl7.org/fhir/SearchParameter/Patient-foo", param.Value.ToString()); await WaitForReindexStatus(reindexJobUri, "Completed"); // When job complete, search for resources using new parameter await ExecuteAndValidateBundle($"Patient?foo:exact={patientName}", expectedPatient.Resource); }
public async Task DocumentOperationPOSTReturn200OnSuccess() { // Setup Composition resource var composition = CreateTestCompositionNoReferences(); var compositionId = "test"; var searchResult = new SearchResult(new List <IResource>() { composition }, 1, 1); _searchMock.Setup(repo => repo.Search( It.Is <IArgumentCollection>(args => args.GetArgument(ArgumentNames.resourceId).ArgumentValue == compositionId), It.IsAny <SearchOptions>())).ReturnsAsync(searchResult); // Create VonkContext for $document (POST / Type level) var testContext = new VonkTestContext(VonkInteraction.instance_custom); testContext.Arguments.AddArguments(new[] { new Argument(ArgumentSource.Path, ArgumentNames.resourceType, "Composition") }); testContext.TestRequest.CustomOperation = "document"; testContext.TestRequest.Method = "POST"; var parameters = new Parameters(); var idValue = new FhirUri(compositionId); var parameterComponent = new Parameters.ParameterComponent { Name = "id" }; parameterComponent.Value = idValue; parameters.Parameter.Add(parameterComponent); testContext.TestRequest.Payload = new RequestPayload(true, parameters.ToIResource()); // Execute $document await _documentService.DocumentTypePOST(testContext); // Check response status testContext.Response.HttpResult.Should().Be(StatusCodes.Status200OK, "$document should succeed with HTTP 200 - OK on test composition"); testContext.Response.Payload.Should().NotBeNull(); var bundleType = testContext.Response.Payload.SelectText("type"); bundleType.Should().Be("document", "Bundle.type should be set to 'document'"); }
public FHIRTerminologyServer(string endpoint) { Endpoint = endpoint; client = new FhirClient(endpoint); valuesetIdParameter = new Parameters.ParameterComponent { Name = "identifier" }; filterParameter = new Parameters.ParameterComponent { Name = "filter" }; countParameter = new Parameters.ParameterComponent { Name = "limit" }; systemParameter = new Parameters.ParameterComponent { Name = "system" }; codeParameter = new Parameters.ParameterComponent { Name = "code" }; //Parameters.ParameterComponent limit = new Parameters.ParameterComponent(); }
public async Task GivenANewSearchParam_WhenReindexingComplete_ThenResourcesSearchedWithNewParamReturned() { var patientName = Guid.NewGuid().ToString().ComputeHash().Substring(28).ToLower(); var patient = new Patient { Name = new List <HumanName> { new HumanName { Family = patientName } } }; var searchParam = Samples.GetJsonSample <SearchParameter>("SearchParameter"); searchParam.Code = "fooCode"; // POST a new patient FhirResponse <Patient> expectedPatient = await Client.CreateAsync(patient); // POST a second patient to show it is filtered and not returned when using the new search parameter await Client.CreateAsync(Samples.GetJsonSample <Patient>("Patient")); // POST a new Search parameter FhirResponse <SearchParameter> searchParamPosted = null; try { searchParamPosted = await Client.CreateAsync(searchParam); } catch (Exception ex) { _output.WriteLine("We encountered an error creating SearchParameter, the next step is to delete and re-add."); _output.WriteLine(ex.Message); // if the SearchParameter exists, we should delete it and recreate it var searchParamBundle = await Client.SearchAsync(ResourceType.SearchParameter, $"url={searchParam.Url}"); if (searchParamBundle.Resource?.Entry[0] != null && searchParamBundle.Resource?.Entry[0].Resource.ResourceType == ResourceType.SearchParameter) { await DeleteSearchParameterAndVerify(searchParamBundle.Resource?.Entry[0].Resource as SearchParameter); searchParamPosted = await Client.CreateAsync(searchParam); } else { throw; } } Uri reindexJobUri; try { // Start a reindex job (_, reindexJobUri) = await Client.PostReindexJobAsync(new Parameters()); } catch (FhirException ex) when(ex.StatusCode == HttpStatusCode.BadRequest && ex.Message.Contains("not enabled")) { Skip.If(!_fixture.IsUsingInProcTestServer, "Reindex is not enabled on this server."); return; } await WaitForReindexStatus(reindexJobUri, "Running", "Completed"); FhirResponse <Parameters> reindexJobResult = await Client.CheckReindexAsync(reindexJobUri); Parameters.ParameterComponent param = reindexJobResult.Resource.Parameter.FirstOrDefault(p => p.Name == "searchParams"); Assert.Contains("http://hl7.org/fhir/SearchParameter/Patient-foo", param.Value.ToString()); reindexJobResult = await WaitForReindexStatus(reindexJobUri, "Completed"); _output.WriteLine("Reindex job is completed, it should have reindexed the Patient resources with foo"); var floatParse = float.TryParse( reindexJobResult.Resource.Parameter.FirstOrDefault(predicate => predicate.Name == "resourcesSuccessfullyReindexed").Value.ToString(), out float resourcesReindexed); _output.WriteLine($"Reindex job is completed, {resourcesReindexed} Patient ressources Reindexed"); Assert.True(floatParse); Assert.True(resourcesReindexed > 0.0); // When job complete, search for resources using new parameter await ExecuteAndValidateBundle($"Patient?{searchParam.Code}:exact={patientName}", expectedPatient.Resource); // Clean up new SearchParameter await DeleteSearchParameterAndVerify(searchParamPosted.Resource); }
public static bool TryGetUriValue(this Parameters.ParameterComponent paramComponent, out Uri uriValue) { Element uriElement = paramComponent?.Value; return(Uri.TryCreate(uriElement?.ToString(), UriKind.RelativeOrAbsolute, out uriValue)); }
public IResourceServiceOutcome Process( IPyroRequestUri RequestUri, ISearchParameterGeneric SearchParameterGeneric, Resource Resource) { IResourceServiceOutcome ResourceServiceOutcome = IResourceServiceOutcomeFactory.CreateResourceServiceOutcome(); var IssueList = new List <OperationOutcome.IssueComponent>(); ISearchParameterService SearchService = ISearchParameterServiceFactory.CreateSearchParameterService(); ISearchParametersServiceOutcome SearchParametersServiceOutcome = SearchService.ProcessBaseSearchParameters(SearchParameterGeneric); if (SearchParametersServiceOutcome.FhirOperationOutcome != null) { ResourceServiceOutcome.ResourceResult = SearchParametersServiceOutcome.FhirOperationOutcome; ResourceServiceOutcome.HttpStatusCode = SearchParametersServiceOutcome.HttpStatusCode; ResourceServiceOutcome.FormatMimeType = SearchParametersServiceOutcome.SearchParameters.Format; return(ResourceServiceOutcome); } if (Resource is Parameters Parameters) { if (Parameters.Parameter != null && Parameters.Parameter.Count > 0) { if (Parameters.Parameter[0].Name.ToLower() == _ParameterName) { if (Parameters.Parameter[0].Resource != null) { if (Parameters.Parameter[0].Resource is QuestionnaireResponse QuestionnaireResponse) { if (QuestionnaireResponse.Meta == null) { QuestionnaireResponse.Meta = new Meta(); } if (QuestionnaireResponse.Meta.Tag == null) { QuestionnaireResponse.Meta.Tag = new List <Coding>(); } QuestionnaireResponse.Meta.Tag.Add(new Coding("https://pyrohealth.net/fhir/CodeSystem/connectathon-answer", "hidden")); if (QuestionnaireResponse.Id == null || string.IsNullOrWhiteSpace(QuestionnaireResponse.Id)) { IRequestMeta RequestMeta = IRequestMetaFactory.CreateRequestMeta().Set($"{FHIRAllTypes.QuestionnaireResponse.GetLiteral()}"); ResourceServiceOutcome = this.IResourceServices.Post(QuestionnaireResponse, RequestMeta); } else { IRequestMeta RequestMeta = IRequestMetaFactory.CreateRequestMeta().Set($"{FHIRAllTypes.QuestionnaireResponse.GetLiteral()}/{QuestionnaireResponse.Id}"); ResourceServiceOutcome = this.IResourceServices.Put(QuestionnaireResponse.Id, QuestionnaireResponse, RequestMeta); } if (ResourceServiceOutcome.SuccessfulTransaction) { Parameters ParametersResult = new Parameters(); ParametersResult.Parameter = new List <Parameters.ParameterComponent>(); var Param = new Parameters.ParameterComponent(); Param.Name = "You answers have been submitted."; ParametersResult.Parameter.Add(Param); var Param2 = new Parameters.ParameterComponent(); Param2.Name = FHIRAllTypes.QuestionnaireResponse.GetLiteral(); Param2.Resource = ResourceServiceOutcome.ResourceResult; ParametersResult.Parameter.Add(Param2); ResourceServiceOutcome.HttpStatusCode = System.Net.HttpStatusCode.OK; ResourceServiceOutcome.ResourceResult = ParametersResult; ResourceServiceOutcome.OperationType = Enum.RestEnum.CrudOperationType.Update; ResourceServiceOutcome.SuccessfulTransaction = true; IRequestMeta RequestMeta = IRequestMetaFactory.CreateRequestMeta().Set($"{FHIRAllTypes.QuestionnaireResponse.GetLiteral()}/{_PrimaryQuestionnaireResponseAnswerResourceId}"); var Answers = this.IResourceServices.GetRead("AngusA1", RequestMeta); QuestionnaireResults QuestionnaireResults = QuestionnaireResponseChecker.Check(Answers.ResourceResult as QuestionnaireResponse, QuestionnaireResponse); } else { Parameters ParametersResult = new Parameters(); ParametersResult.Parameter = new List <Parameters.ParameterComponent>(); var Param = new Parameters.ParameterComponent(); Param.Name = "Oh no, there was an error is submitting your answers."; ParametersResult.Parameter.Add(Param); var Param2 = new Parameters.ParameterComponent(); Param2.Name = FHIRAllTypes.OperationOutcome.GetLiteral(); Param2.Resource = ResourceServiceOutcome.ResourceResult; ParametersResult.Parameter.Add(Param2); ResourceServiceOutcome.HttpStatusCode = System.Net.HttpStatusCode.BadRequest; ResourceServiceOutcome.ResourceResult = ParametersResult; ResourceServiceOutcome.OperationType = Enum.RestEnum.CrudOperationType.Update; ResourceServiceOutcome.SuccessfulTransaction = false; } } else { IssueList.Add(Pyro.Common.Tools.FhirOperationOutcomeSupport.CreateIssue(OperationOutcome.IssueSeverity.Error, OperationOutcome.IssueType.Invalid, $"The ${_OperationName} expects the {FHIRAllTypes.Parameters.GetLiteral()} resource to have one parameter with the 'Name' element equal to '{_ParameterName}' where the Resource element is set to a {FHIRAllTypes.QuestionnaireResponse.GetLiteral()} FHIR Resource. The server found here a Resource of type {Parameters.Parameter[0].Resource.ResourceType}.")); } } else { IssueList.Add(Pyro.Common.Tools.FhirOperationOutcomeSupport.CreateIssue(OperationOutcome.IssueSeverity.Error, OperationOutcome.IssueType.Invalid, $"The ${_OperationName} expects the {FHIRAllTypes.Parameters.GetLiteral()} resource to have one Parameter with the Name element equal to '{_ParameterName}' where the Resource element is set to a {FHIRAllTypes.QuestionnaireResponse.GetLiteral()} FHIR Resource. The server found no {FHIRAllTypes.QuestionnaireResponse.GetLiteral()} Resource")); } } else { IssueList.Add(Pyro.Common.Tools.FhirOperationOutcomeSupport.CreateIssue(OperationOutcome.IssueSeverity.Error, OperationOutcome.IssueType.Invalid, $"The ${_OperationName} expects the {FHIRAllTypes.Parameters.GetLiteral()} resource to have one Parameter with the Name element equal to '{_ParameterName}'.")); } } else { IssueList.Add(Pyro.Common.Tools.FhirOperationOutcomeSupport.CreateIssue(OperationOutcome.IssueSeverity.Error, OperationOutcome.IssueType.Invalid, $"The ${_OperationName} expects the {FHIRAllTypes.Parameters.GetLiteral()} resource to have one Parameter element.")); } } else { IssueList.Add(Pyro.Common.Tools.FhirOperationOutcomeSupport.CreateIssue(OperationOutcome.IssueSeverity.Error, OperationOutcome.IssueType.Invalid, $"The ${_OperationName} expects a FHIR {FHIRAllTypes.Parameters.GetLiteral()} resource as the call body.")); } if (IssueList.Count > 0) { var Op = Common.Tools.FhirOperationOutcomeSupport.Generate(IssueList); ResourceServiceOutcome.HttpStatusCode = System.Net.HttpStatusCode.BadRequest; ResourceServiceOutcome.ResourceResult = Op; ResourceServiceOutcome.OperationType = Enum.RestEnum.CrudOperationType.Update; ResourceServiceOutcome.SuccessfulTransaction = false; return(ResourceServiceOutcome); } else { return(ResourceServiceOutcome); } }
public static bool TryGetStringValue(this Parameters.ParameterComponent paramComponent, out string stringValue) { stringValue = paramComponent?.Value?.ToString(); return(stringValue != null); }
public static bool TryGetBooleanValue(this Parameters.ParameterComponent paramComponent, out bool boolValue) { Element booleanElement = paramComponent?.Value; return(bool.TryParse(booleanElement?.ToString(), out boolValue)); }
public IResourceServiceOutcome Process(ISearchParameterGeneric SearchParameterGeneric) { if (SearchParameterGeneric == null) { throw new NullReferenceException("SearchParameterGeneric cannot be null."); } if (IResourceServices == null) { throw new NullReferenceException("ResourceServices cannot be null."); } try { IResourceServiceOutcome ResourceServiceOutcome = IResourceServiceOutcomeFactory.CreateResourceServiceOutcome(); ISearchParameterService SearchService = ISearchParameterServiceFactory.CreateSearchParameterService(); ISearchParametersServiceOutcome SearchParametersServiceOutcome = SearchService.ProcessBaseSearchParameters(SearchParameterGeneric); if (SearchParametersServiceOutcome.FhirOperationOutcome != null) { ResourceServiceOutcome.ResourceResult = SearchParametersServiceOutcome.FhirOperationOutcome; ResourceServiceOutcome.HttpStatusCode = SearchParametersServiceOutcome.HttpStatusCode; ResourceServiceOutcome.FormatMimeType = SearchParametersServiceOutcome.SearchParameters.Format; return(ResourceServiceOutcome); } var ReturnParametersResource = new Parameters(); ReturnParametersResource.Parameter = new List <Parameters.ParameterComponent>(); foreach (string ResourceName in ModelInfo.SupportedResources) { var ResourceReportParameter = new Parameters.ParameterComponent(); ResourceReportParameter.Name = ResourceName; ResourceReportParameter.Part = new List <Parameters.ParameterComponent>(); ReturnParametersResource.Parameter.Add(ResourceReportParameter); ResourceType CurrentResourceType = Pyro.Common.Tools.ResourceNameResolutionSupport.GetResourceType(ResourceName); int TotalCount = IResourceServices.GetTotalCurrentResourceCount(CurrentResourceType); var TotalCountParameter = new Parameters.ParameterComponent(); TotalCountParameter.Name = $"TotalCount"; TotalCountParameter.Value = new FhirDecimal(TotalCount); ResourceReportParameter.Part.Add(TotalCountParameter); if (TotalCount > 0) { DateTimeOffset?LastUpdated = IResourceServices.GetLastCurrentResourceLastUpdatedValue(CurrentResourceType); if (LastUpdated.HasValue) { var LastUpdatedParameter = new Parameters.ParameterComponent(); LastUpdatedParameter.Name = $"LastUpdated"; LastUpdatedParameter.Value = new FhirDateTime(LastUpdated.Value); ResourceReportParameter.Part.Add(LastUpdatedParameter); } } } ResourceServiceOutcome.HttpStatusCode = System.Net.HttpStatusCode.OK; ResourceServiceOutcome.ResourceResult = ReturnParametersResource; ResourceServiceOutcome.OperationType = Enum.RestEnum.CrudOperationType.Update; ResourceServiceOutcome.SuccessfulTransaction = true; return(ResourceServiceOutcome); } catch (Exception Exec) { throw new Exception("ServerResourceReport exception thrown", Exec); } }
public static bool TryGetUriValue(this Parameters parameters, string name, out Uri uriValue) { Parameters.ParameterComponent param = parameters.GetSingle(name); return(param.TryGetUriValue(out uriValue)); }
public async Task GivenANewSearchParam_WhenReindexingComplete_ThenResourcesSearchedWithNewParamReturned() { var randomName = Guid.NewGuid().ToString().ComputeHash().Substring(0, 14).ToLower(); var searchParam = Samples.GetJsonSample <SearchParameter>("SearchParameter-AppointmentStatus"); searchParam.Name = randomName; searchParam.Url = searchParam.Url.Replace("foo", randomName); searchParam.Code = randomName + "Code"; // POST a new appointment var appointment = Samples.GetJsonSample <Appointment>("Appointment"); appointment.Status = Appointment.AppointmentStatus.Noshow; var tag = new Coding(null, randomName); appointment.Meta = new Meta(); appointment.Meta.Tag.Add(tag); FhirResponse <Appointment> expectedAppointment = await Client.CreateAsync(appointment); // POST a second appointment to show it is filtered and not returned when using the new search parameter var appointment2 = Samples.GetJsonSample <Appointment>("Appointment"); appointment2.Status = Appointment.AppointmentStatus.Booked; appointment2.Meta = new Meta(); appointment2.Meta.Tag.Add(tag); await Client.CreateAsync(appointment2); // POST a new Search parameter FhirResponse <SearchParameter> searchParamPosted = null; try { searchParamPosted = await Client.CreateAsync(searchParam); _output.WriteLine($"SearchParameter is posted {searchParam.Url}"); Uri reindexJobUri; // Start a reindex job (_, reindexJobUri) = await Client.PostReindexJobAsync(new Parameters()); await WaitForReindexStatus(reindexJobUri, "Running", "Completed"); FhirResponse <Parameters> reindexJobResult = await Client.CheckReindexAsync(reindexJobUri); Parameters.ParameterComponent param = reindexJobResult.Resource.Parameter.FirstOrDefault(p => p.Name == "searchParams"); _output.WriteLine("ReindexJobDocument:"); var serializer = new FhirJsonSerializer(); _output.WriteLine(serializer.SerializeToString(reindexJobResult.Resource)); Assert.Contains(searchParamPosted.Resource.Url, param.Value.ToString()); reindexJobResult = await WaitForReindexStatus(reindexJobUri, "Completed"); _output.WriteLine($"Reindex job is completed, it should have reindexed the resources with {randomName}"); var floatParse = float.TryParse( reindexJobResult.Resource.Parameter.FirstOrDefault(predicate => predicate.Name == "resourcesSuccessfullyReindexed").Value.ToString(), out float resourcesReindexed); _output.WriteLine($"Reindex job is completed, {resourcesReindexed} resources Reindexed"); Assert.True(floatParse); Assert.True(resourcesReindexed > 0.0); // When job complete, search for resources using new parameter // When there are multiple instances of the fhir-server running, it could take some time // for the search parameter/reindex updates to propogate to all instances. Hence we are // adding some retries below to account for that delay. int retryCount = 0; bool success = true; do { success = true; retryCount++; try { await ExecuteAndValidateBundle( $"Appointment?{searchParam.Code}={Appointment.AppointmentStatus.Noshow.ToString().ToLower()}&_tag={tag.Code}", expectedAppointment.Resource); } catch (Exception ex) { _output.WriteLine($"Failed to validate bundle: {ex}"); success = false; await Task.Delay(10000); } }while (!success && retryCount < 3); Assert.True(success); } catch (FhirException ex) when(ex.StatusCode == HttpStatusCode.BadRequest && ex.Message.Contains("not enabled")) { Skip.If(!_fixture.IsUsingInProcTestServer, "Reindex is not enabled on this server."); return; } catch (Exception e) { _output.WriteLine($"Exception: {e.Message}"); _output.WriteLine($"Stack Trace: {e.StackTrace}"); throw; } finally { // Clean up new SearchParameter await DeleteSearchParameterAndVerify(searchParamPosted?.Resource); } }
public static Parameters AddPatchParameter(this Parameters parameters, string operationType, string path = "", string name = "", Base value = null) { var component = new Parameters.ParameterComponent { Name = "operation", Part = new List <Parameters.ParameterComponent> { new Parameters.ParameterComponent { Name = "type", Value = new FhirString(operationType), }, }, }; if (!string.IsNullOrEmpty(path)) { component.Part.Add( new Parameters.ParameterComponent { Name = "path", Value = new FhirString(path), }); } if (!string.IsNullOrEmpty(name)) { component.Part.Add( new Parameters.ParameterComponent { Name = "name", Value = new FhirString(name), }); } if (value is not null) { if (value is DataType valueDataType) { component.Part.Add( new Parameters.ParameterComponent { Name = "value", Value = valueDataType, }); } else if (value is Parameters.ParameterComponent valueParameter) { component.Part.Add( new Parameters.ParameterComponent { Name = "value", Part = new List <Parameters.ParameterComponent> { valueParameter }, }); } else { throw new ArgumentOutOfRangeException($"Value must be of type {typeof(DataType)} or {typeof(Parameters.ParameterComponent)}."); } } parameters.Parameter.Add(component); return(parameters); }
public async Task GivenASearchParameterWithMultipleBaseResourceTypes_WhenTargetingReindexJobToResourceType_ThenOnlyTargetedTypesAreReindexed() { var randomName = Guid.NewGuid().ToString().ComputeHash().Substring(0, 14).ToLower(); var searchParam = Samples.GetJsonSample <SearchParameter>("SearchParameter-Resource-idfoo"); searchParam.Name = searchParam.Name.Replace("foo", randomName); searchParam.Url = searchParam.Url.Replace("foo", randomName); searchParam.Code = randomName + "Code"; // POST a new appointment var appointment = Samples.GetJsonSample <Appointment>("Appointment"); FhirResponse <Appointment> expectedAppointment = await Client.CreateAsync(appointment); // POST a second appointment to show it is filtered and not returned when using the new search parameter var appointment2 = Samples.GetJsonSample <Appointment>("Appointment"); await Client.CreateAsync(appointment2); // POST a new patient var patient = new Patient { Name = new List <HumanName> { new HumanName { Family = randomName } } }; FhirResponse <Patient> expectedPatient = await Client.CreateAsync(patient); // POST a new Search parameter FhirResponse <SearchParameter> searchParamPosted = null; try { searchParamPosted = await Client.CreateAsync(searchParam); _output.WriteLine($"SearchParameter is posted {searchParam.Url}"); Uri reindexJobUri; // Start a reindex job var reindexParameters = new Parameters(); reindexParameters.Add("targetResourceTypes", new FhirString("Appointment")); (_, reindexJobUri) = await Client.PostReindexJobAsync(reindexParameters); await WaitForReindexStatus(reindexJobUri, "Completed"); FhirResponse <Parameters> reindexJobResult = await Client.CheckReindexAsync(reindexJobUri); Parameters.ParameterComponent searchParamListParam = reindexJobResult.Resource.Parameter.FirstOrDefault(p => p.Name == "searchParams"); Parameters.ParameterComponent targetResourcesParam = reindexJobResult.Resource.Parameter.FirstOrDefault(p => p.Name == JobRecordProperties.TargetResourceTypes); Parameters.ParameterComponent resourcesParam = reindexJobResult.Resource.Parameter.FirstOrDefault(p => p.Name == JobRecordProperties.Resources); // output reindex job for debugging _output.WriteLine("ReindexJobDocument:"); var serializer = new FhirJsonSerializer(); _output.WriteLine(serializer.SerializeToString(reindexJobResult.Resource)); Assert.Contains(searchParamPosted.Resource.Url, searchParamListParam.Value.ToString()); Assert.Equal("Appointment", targetResourcesParam.Value.ToString()); Assert.Equal("Appointment", resourcesParam.Value.ToString()); _output.WriteLine($"Reindex job is completed, it should have reindexed the resources of type Appointment only."); var floatParse = float.TryParse( reindexJobResult.Resource.Parameter.FirstOrDefault(predicate => predicate.Name == "resourcesSuccessfullyReindexed").Value.ToString(), out float resourcesReindexed); _output.WriteLine($"Reindex job is completed, {resourcesReindexed} resources Reindexed"); Assert.True(floatParse); Assert.True(resourcesReindexed > 0.0); // When job complete, search for resources using new parameter // When there are multiple instances of the fhir-server running, it could take some time // for the search parameter/reindex updates to propogate to all instances. Hence we are // adding some retries below to account for that delay. int retryCount = 0; bool success = true; do { success = true; retryCount++; try { await ExecuteAndValidateBundle( $"Appointment?{searchParam.Code}={expectedAppointment.Resource.Id}", Tuple.Create("x-ms-use-partial-indices", "true"), expectedAppointment.Resource); } catch (Exception ex) { _output.WriteLine($"Failed to validate bundle: {ex}"); success = false; await Task.Delay(10000); } // now searching for patient with same search parameter should not work var searchUrl = $"Patient?{searchParam.Code}={expectedPatient.Resource.Id}"; Bundle bundle = await Client.SearchAsync(searchUrl, Tuple.Create("x-ms-use-partial-indices", "true")); Assert.Empty(bundle.Entry); // finally searching with new SearchParam but without partial header should not use // new search parameter, because it should not be marked fully reindexed searchUrl = $"Patient?{searchParam.Code}={expectedPatient.Resource.Id}"; bundle = await Client.SearchAsync(searchUrl); Assert.DoesNotContain(searchParam.Code, bundle.SelfLink.ToString()); }while (!success && retryCount < 10); Assert.True(success); } catch (FhirException ex) when(ex.StatusCode == HttpStatusCode.BadRequest && ex.Message.Contains("not enabled")) { Skip.If(!_fixture.IsUsingInProcTestServer, "Reindex is not enabled on this server."); return; } catch (Exception e) { _output.WriteLine($"Exception: {e.Message}"); _output.WriteLine($"Stack Trace: {e.StackTrace}"); throw; } finally { // Clean up new SearchParameter await DeleteSearchParameterAndVerify(searchParamPosted?.Resource); } }
public async Task GivenASearchParam_WhenUpdatingParam_ThenResourcesIndexedWithUpdatedParam() { var randomName = Guid.NewGuid().ToString().ComputeHash().Substring(28).ToLower(); var patient = new Patient { Name = new List <HumanName> { new HumanName { Family = randomName } } }; var searchParam = Samples.GetJsonSample <SearchParameter>("SearchParameter"); searchParam.Name = randomName; searchParam.Url = searchParam.Url.Replace("foo", randomName); searchParam.Code = randomName; searchParam.Id = randomName; // POST a new patient FhirResponse <Patient> expectedPatient = await Client.CreateAsync(patient); // POST a new Search parameter FhirResponse <SearchParameter> searchParamPosted = null; try { searchParamPosted = await Client.CreateAsync(searchParam); // now update the new search parameter var randomNameUpdated = randomName + "U"; searchParamPosted.Resource.Name = randomNameUpdated; searchParamPosted.Resource.Url = "http://hl7.org/fhir/SearchParameter/Patient-" + randomNameUpdated; searchParamPosted.Resource.Code = randomNameUpdated; searchParamPosted = await Client.UpdateAsync(searchParamPosted.Resource); Uri reindexJobUri; FhirResponse <Parameters> reindexJobResult; // Reindex just a single patient, so we can try searching with a partially indexed search param (reindexJobResult, reindexJobUri) = await Client.PostReindexJobAsync(new Parameters(), $"Patient/{expectedPatient.Resource.Id}/"); Parameters.ParameterComponent param = reindexJobResult.Resource.Parameter.FirstOrDefault(p => p.Name == randomNameUpdated); if (param == null) { _output.WriteLine($"Parameter with name equal to randomly generated name of this test case: {randomNameUpdated} not found in reindex result."); } Assert.NotNull(param); Assert.Equal(randomName, param.Value.ToString()); // When job complete, search for resources using new parameter await ExecuteAndValidateBundle( $"Patient?{searchParamPosted.Resource.Code}:exact={randomName}", Tuple.Create("x-ms-use-partial-indices", "true"), expectedPatient.Resource); } catch (FhirException ex) when(ex.StatusCode == HttpStatusCode.BadRequest && ex.Message.Contains("not enabled")) { Skip.If(!_fixture.IsUsingInProcTestServer, "Reindex is not enabled on this server."); return; } catch (Exception e) { _output.WriteLine($"Exception: {e.Message}"); _output.WriteLine($"Stack Trace: {e.StackTrace}"); throw; } finally { // Clean up new SearchParameter await DeleteSearchParameterAndVerify(searchParamPosted?.Resource); } }
/// <summary> /// Serialize a FHIR Parameters#Parameter into JSON /// </summary> public static void SerializeJson(this Parameters.ParameterComponent current, Utf8JsonWriter writer, JsonSerializerOptions options, bool includeStartObject = true) { if (includeStartObject) { writer.WriteStartObject(); } // Component: Parameters#Parameter, Export: ParameterComponent, Base: BackboneElement (BackboneElement) ((Hl7.Fhir.Model.BackboneElement)current).SerializeJson(writer, options, false); writer.WriteString("name", current.NameElement.Value); if (current.Value != null) { switch (current.Value) { case Base64Binary v_Base64Binary: if (v_Base64Binary != null) { if (v_Base64Binary.Value != null) { writer.WriteString("valueBase64Binary", System.Convert.ToBase64String(v_Base64Binary.Value)); } if (v_Base64Binary.HasExtensions() || (!string.IsNullOrEmpty(v_Base64Binary.ElementId))) { JsonStreamUtilities.SerializeExtensionList(writer, options, "_valueBase64Binary", false, v_Base64Binary.Extension, v_Base64Binary.ElementId); } } break; case FhirBoolean v_FhirBoolean: if (v_FhirBoolean != null) { if (v_FhirBoolean.Value != null) { writer.WriteBoolean("valueBoolean", (bool)v_FhirBoolean.Value); } if (v_FhirBoolean.HasExtensions() || (!string.IsNullOrEmpty(v_FhirBoolean.ElementId))) { JsonStreamUtilities.SerializeExtensionList(writer, options, "_valueBoolean", false, v_FhirBoolean.Extension, v_FhirBoolean.ElementId); } } break; case Canonical v_Canonical: if (v_Canonical != null) { if (!string.IsNullOrEmpty(v_Canonical.Value)) { writer.WriteString("valueCanonical", v_Canonical.Value); } if (v_Canonical.HasExtensions() || (!string.IsNullOrEmpty(v_Canonical.ElementId))) { JsonStreamUtilities.SerializeExtensionList(writer, options, "_valueCanonical", false, v_Canonical.Extension, v_Canonical.ElementId); } } break; case Code v_Code: if (v_Code != null) { if (!string.IsNullOrEmpty(v_Code.Value)) { writer.WriteString("valueCode", v_Code.Value.Trim()); } if (v_Code.HasExtensions() || (!string.IsNullOrEmpty(v_Code.ElementId))) { JsonStreamUtilities.SerializeExtensionList(writer, options, "_valueCode", false, v_Code.Extension, v_Code.ElementId); } } break; case Date v_Date: if (v_Date != null) { if (!string.IsNullOrEmpty(v_Date.Value)) { writer.WriteString("valueDate", v_Date.Value); } if (v_Date.HasExtensions() || (!string.IsNullOrEmpty(v_Date.ElementId))) { JsonStreamUtilities.SerializeExtensionList(writer, options, "_valueDate", false, v_Date.Extension, v_Date.ElementId); } } break; case FhirDateTime v_FhirDateTime: if (v_FhirDateTime != null) { if (!string.IsNullOrEmpty(v_FhirDateTime.Value)) { writer.WriteString("valueDateTime", v_FhirDateTime.Value); } if (v_FhirDateTime.HasExtensions() || (!string.IsNullOrEmpty(v_FhirDateTime.ElementId))) { JsonStreamUtilities.SerializeExtensionList(writer, options, "_valueDateTime", false, v_FhirDateTime.Extension, v_FhirDateTime.ElementId); } } break; case FhirDecimal v_FhirDecimal: if (v_FhirDecimal != null) { if (v_FhirDecimal.Value != null) { writer.WriteNumber("valueDecimal", (decimal)v_FhirDecimal.Value); } if (v_FhirDecimal.HasExtensions() || (!string.IsNullOrEmpty(v_FhirDecimal.ElementId))) { JsonStreamUtilities.SerializeExtensionList(writer, options, "_valueDecimal", false, v_FhirDecimal.Extension, v_FhirDecimal.ElementId); } } break; case Id v_Id: if (v_Id != null) { if (!string.IsNullOrEmpty(v_Id.Value)) { writer.WriteString("valueId", v_Id.Value); } if (v_Id.HasExtensions() || (!string.IsNullOrEmpty(v_Id.ElementId))) { JsonStreamUtilities.SerializeExtensionList(writer, options, "_valueId", false, v_Id.Extension, v_Id.ElementId); } } break; case Instant v_Instant: if (v_Instant != null) { if (v_Instant.Value != null) { writer.WriteString("valueInstant", ((DateTimeOffset)v_Instant.Value).ToString("yyyy-MM-dd'T'HH:mm:ss.FFFFFFFK", System.Globalization.CultureInfo.InvariantCulture)); } if (v_Instant.HasExtensions() || (!string.IsNullOrEmpty(v_Instant.ElementId))) { JsonStreamUtilities.SerializeExtensionList(writer, options, "_valueInstant", false, v_Instant.Extension, v_Instant.ElementId); } } break; case Integer v_Integer: if (v_Integer != null) { if (v_Integer.Value != null) { writer.WriteNumber("valueInteger", (int)v_Integer.Value); } if (v_Integer.HasExtensions() || (!string.IsNullOrEmpty(v_Integer.ElementId))) { JsonStreamUtilities.SerializeExtensionList(writer, options, "_valueInteger", false, v_Integer.Extension, v_Integer.ElementId); } } break; case Markdown v_Markdown: if (v_Markdown != null) { if (!string.IsNullOrEmpty(v_Markdown.Value)) { writer.WriteString("valueMarkdown", v_Markdown.Value); } if (v_Markdown.HasExtensions() || (!string.IsNullOrEmpty(v_Markdown.ElementId))) { JsonStreamUtilities.SerializeExtensionList(writer, options, "_valueMarkdown", false, v_Markdown.Extension, v_Markdown.ElementId); } } break; case Oid v_Oid: if (v_Oid != null) { if (!string.IsNullOrEmpty(v_Oid.Value)) { writer.WriteString("valueOid", v_Oid.Value); } if (v_Oid.HasExtensions() || (!string.IsNullOrEmpty(v_Oid.ElementId))) { JsonStreamUtilities.SerializeExtensionList(writer, options, "_valueOid", false, v_Oid.Extension, v_Oid.ElementId); } } break; case PositiveInt v_PositiveInt: if (v_PositiveInt != null) { if (v_PositiveInt.Value != null) { writer.WriteNumber("valuePositiveInt", (int)v_PositiveInt.Value); } if (v_PositiveInt.HasExtensions() || (!string.IsNullOrEmpty(v_PositiveInt.ElementId))) { JsonStreamUtilities.SerializeExtensionList(writer, options, "_valuePositiveInt", false, v_PositiveInt.Extension, v_PositiveInt.ElementId); } } break; case FhirString v_FhirString: if (v_FhirString != null) { if (!string.IsNullOrEmpty(v_FhirString.Value)) { writer.WriteString("valueString", v_FhirString.Value); } if (v_FhirString.HasExtensions() || (!string.IsNullOrEmpty(v_FhirString.ElementId))) { JsonStreamUtilities.SerializeExtensionList(writer, options, "_valueString", false, v_FhirString.Extension, v_FhirString.ElementId); } } break; case Time v_Time: writer.WritePropertyName("valueTime"); v_Time.SerializeJson(writer, options); break; case UnsignedInt v_UnsignedInt: if (v_UnsignedInt != null) { if (v_UnsignedInt.Value != null) { writer.WriteNumber("valueUnsignedInt", (int)v_UnsignedInt.Value); } if (v_UnsignedInt.HasExtensions() || (!string.IsNullOrEmpty(v_UnsignedInt.ElementId))) { JsonStreamUtilities.SerializeExtensionList(writer, options, "_valueUnsignedInt", false, v_UnsignedInt.Extension, v_UnsignedInt.ElementId); } } break; case FhirUri v_FhirUri: if (v_FhirUri != null) { if (!string.IsNullOrEmpty(v_FhirUri.Value)) { writer.WriteString("valueUri", v_FhirUri.Value); } if (v_FhirUri.HasExtensions() || (!string.IsNullOrEmpty(v_FhirUri.ElementId))) { JsonStreamUtilities.SerializeExtensionList(writer, options, "_valueUri", false, v_FhirUri.Extension, v_FhirUri.ElementId); } } break; case FhirUrl v_FhirUrl: if (v_FhirUrl != null) { if (!string.IsNullOrEmpty(v_FhirUrl.Value)) { writer.WriteString("valueUrl", v_FhirUrl.Value); } if (v_FhirUrl.HasExtensions() || (!string.IsNullOrEmpty(v_FhirUrl.ElementId))) { JsonStreamUtilities.SerializeExtensionList(writer, options, "_valueUrl", false, v_FhirUrl.Extension, v_FhirUrl.ElementId); } } break; case Uuid v_Uuid: if (v_Uuid != null) { if (!string.IsNullOrEmpty(v_Uuid.Value)) { writer.WriteString("valueUuid", v_Uuid.Value); } if (v_Uuid.HasExtensions() || (!string.IsNullOrEmpty(v_Uuid.ElementId))) { JsonStreamUtilities.SerializeExtensionList(writer, options, "_valueUuid", false, v_Uuid.Extension, v_Uuid.ElementId); } } break; case Address v_Address: writer.WritePropertyName("valueAddress"); v_Address.SerializeJson(writer, options); break; case Age v_Age: writer.WritePropertyName("valueAge"); v_Age.SerializeJson(writer, options); break; case Annotation v_Annotation: writer.WritePropertyName("valueAnnotation"); v_Annotation.SerializeJson(writer, options); break; case Attachment v_Attachment: writer.WritePropertyName("valueAttachment"); v_Attachment.SerializeJson(writer, options); break; case CodeableConcept v_CodeableConcept: writer.WritePropertyName("valueCodeableConcept"); v_CodeableConcept.SerializeJson(writer, options); break; case Coding v_Coding: writer.WritePropertyName("valueCoding"); v_Coding.SerializeJson(writer, options); break; case ContactPoint v_ContactPoint: writer.WritePropertyName("valueContactPoint"); v_ContactPoint.SerializeJson(writer, options); break; case Count v_Count: writer.WritePropertyName("valueCount"); v_Count.SerializeJson(writer, options); break; case Distance v_Distance: writer.WritePropertyName("valueDistance"); v_Distance.SerializeJson(writer, options); break; case Duration v_Duration: writer.WritePropertyName("valueDuration"); v_Duration.SerializeJson(writer, options); break; case HumanName v_HumanName: writer.WritePropertyName("valueHumanName"); v_HumanName.SerializeJson(writer, options); break; case Identifier v_Identifier: writer.WritePropertyName("valueIdentifier"); v_Identifier.SerializeJson(writer, options); break; case Money v_Money: writer.WritePropertyName("valueMoney"); v_Money.SerializeJson(writer, options); break; case Period v_Period: writer.WritePropertyName("valuePeriod"); v_Period.SerializeJson(writer, options); break; case Quantity v_Quantity: writer.WritePropertyName("valueQuantity"); v_Quantity.SerializeJson(writer, options); break; case Range v_Range: writer.WritePropertyName("valueRange"); v_Range.SerializeJson(writer, options); break; case Ratio v_Ratio: writer.WritePropertyName("valueRatio"); v_Ratio.SerializeJson(writer, options); break; case ResourceReference v_ResourceReference: writer.WritePropertyName("valueReference"); v_ResourceReference.SerializeJson(writer, options); break; case SampledData v_SampledData: writer.WritePropertyName("valueSampledData"); v_SampledData.SerializeJson(writer, options); break; case Signature v_Signature: writer.WritePropertyName("valueSignature"); v_Signature.SerializeJson(writer, options); break; case Timing v_Timing: writer.WritePropertyName("valueTiming"); v_Timing.SerializeJson(writer, options); break; case ContactDetail v_ContactDetail: writer.WritePropertyName("valueContactDetail"); v_ContactDetail.SerializeJson(writer, options); break; case Contributor v_Contributor: writer.WritePropertyName("valueContributor"); v_Contributor.SerializeJson(writer, options); break; case DataRequirement v_DataRequirement: writer.WritePropertyName("valueDataRequirement"); v_DataRequirement.SerializeJson(writer, options); break; case Expression v_Expression: writer.WritePropertyName("valueExpression"); v_Expression.SerializeJson(writer, options); break; case ParameterDefinition v_ParameterDefinition: writer.WritePropertyName("valueParameterDefinition"); v_ParameterDefinition.SerializeJson(writer, options); break; case RelatedArtifact v_RelatedArtifact: writer.WritePropertyName("valueRelatedArtifact"); v_RelatedArtifact.SerializeJson(writer, options); break; case TriggerDefinition v_TriggerDefinition: writer.WritePropertyName("valueTriggerDefinition"); v_TriggerDefinition.SerializeJson(writer, options); break; case UsageContext v_UsageContext: writer.WritePropertyName("valueUsageContext"); v_UsageContext.SerializeJson(writer, options); break; case Dosage v_Dosage: writer.WritePropertyName("valueDosage"); v_Dosage.SerializeJson(writer, options); break; case Meta v_Meta: writer.WritePropertyName("valueMeta"); v_Meta.SerializeJson(writer, options); break; } } if (current.Resource != null) { writer.WritePropertyName("resource"); JsonSerializer.Serialize <object>(writer, (Hl7.Fhir.Model.Resource)current.Resource, options); } if ((current.Part != null) && (current.Part.Count != 0)) { writer.WritePropertyName("part"); writer.WriteStartArray(); foreach (Parameters.ParameterComponent val in current.Part) { val.SerializeJson(writer, options, true); } writer.WriteEndArray(); } if (includeStartObject) { writer.WriteEndObject(); } }
/// <summary> /// Deserialize JSON into a FHIR Parameters#Parameter /// </summary> public static void DeserializeJsonProperty(this Parameters.ParameterComponent current, ref Utf8JsonReader reader, JsonSerializerOptions options, string propertyName) { switch (propertyName) { case "name": if (reader.TokenType == JsonTokenType.Null) { current.NameElement = new FhirString(); reader.Skip(); } else { current.NameElement = new FhirString(reader.GetString()); } break; case "_name": if (current.NameElement == null) { current.NameElement = new FhirString(); } ((Hl7.Fhir.Model.Element)current.NameElement).DeserializeJson(ref reader, options); break; case "valueBase64Binary": if (reader.TokenType == JsonTokenType.Null) { current.Value = new Base64Binary(); reader.Skip(); } else { current.Value = new Base64Binary(System.Convert.FromBase64String(reader.GetString())); } break; case "_valueBase64Binary": if (current.Value == null) { current.Value = new Base64Binary(); } ((Hl7.Fhir.Model.Element)current.Value).DeserializeJson(ref reader, options); break; case "valueBoolean": if (reader.TokenType == JsonTokenType.Null) { current.Value = new FhirBoolean(); reader.Skip(); } else { current.Value = new FhirBoolean(reader.GetBoolean()); } break; case "_valueBoolean": if (current.Value == null) { current.Value = new FhirBoolean(); } ((Hl7.Fhir.Model.Element)current.Value).DeserializeJson(ref reader, options); break; case "valueCanonical": if (reader.TokenType == JsonTokenType.Null) { current.Value = new Canonical(); reader.Skip(); } else { current.Value = new Canonical(reader.GetString()); } break; case "_valueCanonical": if (current.Value == null) { current.Value = new Canonical(); } ((Hl7.Fhir.Model.Element)current.Value).DeserializeJson(ref reader, options); break; case "valueCode": if (reader.TokenType == JsonTokenType.Null) { current.Value = new Code(); reader.Skip(); } else { current.Value = new Code(reader.GetString()); } break; case "_valueCode": if (current.Value == null) { current.Value = new Code(); } ((Hl7.Fhir.Model.Element)current.Value).DeserializeJson(ref reader, options); break; case "valueDate": if (reader.TokenType == JsonTokenType.Null) { current.Value = new Date(); reader.Skip(); } else { current.Value = new Date(reader.GetString()); } break; case "_valueDate": if (current.Value == null) { current.Value = new Date(); } ((Hl7.Fhir.Model.Element)current.Value).DeserializeJson(ref reader, options); break; case "valueDateTime": if (reader.TokenType == JsonTokenType.Null) { current.Value = new FhirDateTime(); reader.Skip(); } else { current.Value = new FhirDateTime(reader.GetString()); } break; case "_valueDateTime": if (current.Value == null) { current.Value = new FhirDateTime(); } ((Hl7.Fhir.Model.Element)current.Value).DeserializeJson(ref reader, options); break; case "valueDecimal": if (reader.TokenType == JsonTokenType.Null) { current.Value = new FhirDecimal(); reader.Skip(); } else { current.Value = new FhirDecimal(reader.GetDecimal()); } break; case "_valueDecimal": if (current.Value == null) { current.Value = new FhirDecimal(); } ((Hl7.Fhir.Model.Element)current.Value).DeserializeJson(ref reader, options); break; case "valueId": if (reader.TokenType == JsonTokenType.Null) { current.Value = new Id(); reader.Skip(); } else { current.Value = new Id(reader.GetString()); } break; case "_valueId": if (current.Value == null) { current.Value = new Id(); } ((Hl7.Fhir.Model.Element)current.Value).DeserializeJson(ref reader, options); break; case "valueInstant": if (reader.TokenType == JsonTokenType.Null) { current.Value = new Instant(); reader.Skip(); } else { current.Value = new Instant(DateTimeOffset.Parse(reader.GetString())); } break; case "_valueInstant": if (current.Value == null) { current.Value = new Instant(); } ((Hl7.Fhir.Model.Element)current.Value).DeserializeJson(ref reader, options); break; case "valueInteger": if (reader.TokenType == JsonTokenType.Null) { current.Value = new Integer(); reader.Skip(); } else { current.Value = new Integer(reader.GetInt32()); } break; case "_valueInteger": if (current.Value == null) { current.Value = new Integer(); } ((Hl7.Fhir.Model.Element)current.Value).DeserializeJson(ref reader, options); break; case "valueMarkdown": if (reader.TokenType == JsonTokenType.Null) { current.Value = new Markdown(); reader.Skip(); } else { current.Value = new Markdown(reader.GetString()); } break; case "_valueMarkdown": if (current.Value == null) { current.Value = new Markdown(); } ((Hl7.Fhir.Model.Element)current.Value).DeserializeJson(ref reader, options); break; case "valueOid": if (reader.TokenType == JsonTokenType.Null) { current.Value = new Oid(); reader.Skip(); } else { current.Value = new Oid(reader.GetString()); } break; case "_valueOid": if (current.Value == null) { current.Value = new Oid(); } ((Hl7.Fhir.Model.Element)current.Value).DeserializeJson(ref reader, options); break; case "valuePositiveInt": if (reader.TokenType == JsonTokenType.Null) { current.Value = new PositiveInt(); reader.Skip(); } else { current.Value = new PositiveInt(reader.GetInt32()); } break; case "_valuePositiveInt": if (current.Value == null) { current.Value = new PositiveInt(); } ((Hl7.Fhir.Model.Element)current.Value).DeserializeJson(ref reader, options); break; case "valueString": if (reader.TokenType == JsonTokenType.Null) { current.Value = new FhirString(); reader.Skip(); } else { current.Value = new FhirString(reader.GetString()); } break; case "_valueString": if (current.Value == null) { current.Value = new FhirString(); } ((Hl7.Fhir.Model.Element)current.Value).DeserializeJson(ref reader, options); break; case "valueTime": current.Value = new Hl7.Fhir.Model.Time(); ((Hl7.Fhir.Model.Time)current.Value).DeserializeJson(ref reader, options); break; case "valueUnsignedInt": if (reader.TokenType == JsonTokenType.Null) { current.Value = new UnsignedInt(); reader.Skip(); } else { current.Value = new UnsignedInt(reader.GetInt32()); } break; case "_valueUnsignedInt": if (current.Value == null) { current.Value = new UnsignedInt(); } ((Hl7.Fhir.Model.Element)current.Value).DeserializeJson(ref reader, options); break; case "valueUri": if (reader.TokenType == JsonTokenType.Null) { current.Value = new FhirUri(); reader.Skip(); } else { current.Value = new FhirUri(reader.GetString()); } break; case "_valueUri": if (current.Value == null) { current.Value = new FhirUri(); } ((Hl7.Fhir.Model.Element)current.Value).DeserializeJson(ref reader, options); break; case "valueUrl": if (reader.TokenType == JsonTokenType.Null) { current.Value = new FhirUrl(); reader.Skip(); } else { current.Value = new FhirUrl(reader.GetString()); } break; case "_valueUrl": if (current.Value == null) { current.Value = new FhirUrl(); } ((Hl7.Fhir.Model.Element)current.Value).DeserializeJson(ref reader, options); break; case "valueUuid": if (reader.TokenType == JsonTokenType.Null) { current.Value = new Uuid(); reader.Skip(); } else { current.Value = new Uuid(reader.GetString()); } break; case "_valueUuid": if (current.Value == null) { current.Value = new Uuid(); } ((Hl7.Fhir.Model.Element)current.Value).DeserializeJson(ref reader, options); break; case "valueAddress": current.Value = new Hl7.Fhir.Model.Address(); ((Hl7.Fhir.Model.Address)current.Value).DeserializeJson(ref reader, options); break; case "valueAge": current.Value = new Hl7.Fhir.Model.Age(); ((Hl7.Fhir.Model.Age)current.Value).DeserializeJson(ref reader, options); break; case "valueAnnotation": current.Value = new Hl7.Fhir.Model.Annotation(); ((Hl7.Fhir.Model.Annotation)current.Value).DeserializeJson(ref reader, options); break; case "valueAttachment": current.Value = new Hl7.Fhir.Model.Attachment(); ((Hl7.Fhir.Model.Attachment)current.Value).DeserializeJson(ref reader, options); break; case "valueCodeableConcept": current.Value = new Hl7.Fhir.Model.CodeableConcept(); ((Hl7.Fhir.Model.CodeableConcept)current.Value).DeserializeJson(ref reader, options); break; case "valueCoding": current.Value = new Hl7.Fhir.Model.Coding(); ((Hl7.Fhir.Model.Coding)current.Value).DeserializeJson(ref reader, options); break; case "valueContactPoint": current.Value = new Hl7.Fhir.Model.ContactPoint(); ((Hl7.Fhir.Model.ContactPoint)current.Value).DeserializeJson(ref reader, options); break; case "valueCount": current.Value = new Hl7.Fhir.Model.Count(); ((Hl7.Fhir.Model.Count)current.Value).DeserializeJson(ref reader, options); break; case "valueDistance": current.Value = new Hl7.Fhir.Model.Distance(); ((Hl7.Fhir.Model.Distance)current.Value).DeserializeJson(ref reader, options); break; case "valueDuration": current.Value = new Hl7.Fhir.Model.Duration(); ((Hl7.Fhir.Model.Duration)current.Value).DeserializeJson(ref reader, options); break; case "valueHumanName": current.Value = new Hl7.Fhir.Model.HumanName(); ((Hl7.Fhir.Model.HumanName)current.Value).DeserializeJson(ref reader, options); break; case "valueIdentifier": current.Value = new Hl7.Fhir.Model.Identifier(); ((Hl7.Fhir.Model.Identifier)current.Value).DeserializeJson(ref reader, options); break; case "valueMoney": current.Value = new Hl7.Fhir.Model.Money(); ((Hl7.Fhir.Model.Money)current.Value).DeserializeJson(ref reader, options); break; case "valuePeriod": current.Value = new Hl7.Fhir.Model.Period(); ((Hl7.Fhir.Model.Period)current.Value).DeserializeJson(ref reader, options); break; case "valueQuantity": current.Value = new Hl7.Fhir.Model.Quantity(); ((Hl7.Fhir.Model.Quantity)current.Value).DeserializeJson(ref reader, options); break; case "valueRange": current.Value = new Hl7.Fhir.Model.Range(); ((Hl7.Fhir.Model.Range)current.Value).DeserializeJson(ref reader, options); break; case "valueRatio": current.Value = new Hl7.Fhir.Model.Ratio(); ((Hl7.Fhir.Model.Ratio)current.Value).DeserializeJson(ref reader, options); break; case "valueReference": current.Value = new Hl7.Fhir.Model.ResourceReference(); ((Hl7.Fhir.Model.ResourceReference)current.Value).DeserializeJson(ref reader, options); break; case "valueSampledData": current.Value = new Hl7.Fhir.Model.SampledData(); ((Hl7.Fhir.Model.SampledData)current.Value).DeserializeJson(ref reader, options); break; case "valueSignature": current.Value = new Hl7.Fhir.Model.Signature(); ((Hl7.Fhir.Model.Signature)current.Value).DeserializeJson(ref reader, options); break; case "valueTiming": current.Value = new Hl7.Fhir.Model.Timing(); ((Hl7.Fhir.Model.Timing)current.Value).DeserializeJson(ref reader, options); break; case "valueContactDetail": current.Value = new Hl7.Fhir.Model.ContactDetail(); ((Hl7.Fhir.Model.ContactDetail)current.Value).DeserializeJson(ref reader, options); break; case "valueContributor": current.Value = new Hl7.Fhir.Model.Contributor(); ((Hl7.Fhir.Model.Contributor)current.Value).DeserializeJson(ref reader, options); break; case "valueDataRequirement": current.Value = new Hl7.Fhir.Model.DataRequirement(); ((Hl7.Fhir.Model.DataRequirement)current.Value).DeserializeJson(ref reader, options); break; case "valueExpression": current.Value = new Hl7.Fhir.Model.Expression(); ((Hl7.Fhir.Model.Expression)current.Value).DeserializeJson(ref reader, options); break; case "valueParameterDefinition": current.Value = new Hl7.Fhir.Model.ParameterDefinition(); ((Hl7.Fhir.Model.ParameterDefinition)current.Value).DeserializeJson(ref reader, options); break; case "valueRelatedArtifact": current.Value = new Hl7.Fhir.Model.RelatedArtifact(); ((Hl7.Fhir.Model.RelatedArtifact)current.Value).DeserializeJson(ref reader, options); break; case "valueTriggerDefinition": current.Value = new Hl7.Fhir.Model.TriggerDefinition(); ((Hl7.Fhir.Model.TriggerDefinition)current.Value).DeserializeJson(ref reader, options); break; case "valueUsageContext": current.Value = new Hl7.Fhir.Model.UsageContext(); ((Hl7.Fhir.Model.UsageContext)current.Value).DeserializeJson(ref reader, options); break; case "valueDosage": current.Value = new Hl7.Fhir.Model.Dosage(); ((Hl7.Fhir.Model.Dosage)current.Value).DeserializeJson(ref reader, options); break; case "valueMeta": current.Value = new Hl7.Fhir.Model.Meta(); ((Hl7.Fhir.Model.Meta)current.Value).DeserializeJson(ref reader, options); break; case "resource": current.Resource = JsonStreamResourceConverter.PolymorphicRead(ref reader, typeof(Resource), options); break; case "part": if ((reader.TokenType != JsonTokenType.StartArray) || (!reader.Read())) { throw new JsonException($"ParameterComponent error reading 'part' expected StartArray, found {reader.TokenType}! depth: {reader.CurrentDepth}, pos: {reader.BytesConsumed}"); } current.Part = new List <Parameters.ParameterComponent>(); while (reader.TokenType != JsonTokenType.EndArray) { Hl7.Fhir.Model.Parameters.ParameterComponent v_Part = new Hl7.Fhir.Model.Parameters.ParameterComponent(); v_Part.DeserializeJson(ref reader, options); current.Part.Add(v_Part); if (!reader.Read()) { throw new JsonException($"ParameterComponent error reading 'part' array, read failed! depth: {reader.CurrentDepth}, pos: {reader.BytesConsumed}"); } if (reader.TokenType == JsonTokenType.EndObject) { reader.Read(); } } if (current.Part.Count == 0) { current.Part = null; } break; // Complex: parameter, Export: ParameterComponent, Base: BackboneElement default: ((Hl7.Fhir.Model.BackboneElement)current).DeserializeJsonProperty(ref reader, options, propertyName); break; } }
public async Task GivenASearchParam_WhenUpdatingParam_ThenResourcesIndexedWithUpdatedParam() { var patientName = Guid.NewGuid().ToString().ComputeHash().Substring(28).ToLower(); var patient = new Patient { Name = new List <HumanName> { new HumanName { Family = patientName } } }; var searchParam = Samples.GetJsonSample <SearchParameter>("SearchParameter"); // POST a new patient FhirResponse <Patient> expectedPatient = await Client.CreateAsync(patient); // POST a new Search parameter FhirResponse <SearchParameter> searchParamPosted = null; try { searchParamPosted = await Client.CreateAsync(searchParam); } catch (FhirException) { // if the SearchParameter exists, we should delete it and recreate it var searchParamBundle = await Client.SearchAsync(ResourceType.SearchParameter, $"url={searchParam.Url}"); if (searchParamBundle.Resource?.Entry[0] != null && searchParamBundle.Resource?.Entry[0].Resource.ResourceType == ResourceType.SearchParameter) { await DeleteSearchParameterAndVerify(searchParamBundle.Resource?.Entry[0].Resource as SearchParameter); searchParamPosted = await Client.CreateAsync(searchParam); } else { throw; } } // now update the new search parameter searchParamPosted.Resource.Name = "foo2"; searchParamPosted.Resource.Url = "http://hl7.org/fhir/SearchParameter/Patient-foo2"; searchParamPosted.Resource.Code = "foo2"; searchParamPosted = await Client.UpdateAsync(searchParamPosted.Resource); Uri reindexJobUri; FhirResponse <Parameters> reindexJobResult; try { // Reindex just a single patient, so we can try searching with a partially indexed search param (reindexJobResult, reindexJobUri) = await Client.PostReindexJobAsync(new Parameters(), $"Patient/{expectedPatient.Resource.Id}/"); Parameters.ParameterComponent param = reindexJobResult.Resource.Parameter.FirstOrDefault(p => p.Name == "foo2"); Assert.Equal(patientName, param.Value.ToString()); } catch (FhirException ex) when(ex.StatusCode == HttpStatusCode.BadRequest && ex.Message.Contains("not enabled")) { Skip.If(!_fixture.IsUsingInProcTestServer, "Reindex is not enabled on this server."); return; } // When job complete, search for resources using new parameter await ExecuteAndValidateBundle($"Patient?foo2:exact={patientName}", Tuple.Create("x-ms-use-partial-indices", "true"), expectedPatient.Resource); // Clean up new SearchParameter await DeleteSearchParameterAndVerify(searchParamPosted.Resource); }
public void Test_DeleteHistoryIndexes() { FhirClient clientFhir = new FhirClient(StaticTestData.FhirEndpoint(), false); clientFhir.Timeout = 1000 * 720; // give the call a while to execute (particularly while debugging). string PatientOneResourceId = Guid.NewGuid().ToString(); string PatientOneMRNIdentifer = Guid.NewGuid().ToString(); //Add a Patient resource by Create Patient PatientOne = new Patient(); PatientOne.Id = PatientOneResourceId; PatientOne.Name.Add(HumanName.ForFamily("TestPatient").WithGiven("Test")); PatientOne.BirthDateElement = new Date("1979-09-30"); PatientOne.Identifier.Add(new Identifier(StaticTestData.TestIdentiferSystem, PatientOneMRNIdentifer)); PatientOne.Gender = AdministrativeGender.Unknown; Patient PatientResult = null; try { PatientResult = clientFhir.Update <Patient>(PatientOne); } catch (Exception Exec) { Assert.True(false, "Exception thrown on Patient resource Update: " + Exec.Message); } Assert.NotNull(PatientResult, "Resource create by Updated returned resource of null"); PatientResult = null; //Update the patient again to ensure there are History indexes to delete try { PatientResult = clientFhir.Update <Patient>(PatientOne); } catch (Exception Exec) { Assert.True(false, "Exception thrown on Patient resource Update: " + Exec.Message); } Assert.NotNull(PatientResult, "Resource create by Updated returned resource of null"); //------------------------------------------------------------------------------------ // ------------ Base Operation, limited types by parameters -------------------------- //------------------------------------------------------------------------------------ //Now setup to use the base operation $delete-history-indexes //Parameter Resource Parameters ParametersIn = new Parameters(); //ParametersIn.Id = Guid.NewGuid().ToString(); ParametersIn.Parameter = new List <Parameters.ParameterComponent>(); var ParamOne = new Parameters.ParameterComponent(); ParametersIn.Parameter.Add(ParamOne); ParamOne.Name = "ResourceType"; ParamOne.Value = new FhirString(FHIRAllTypes.Patient.GetLiteral()); Parameters ParametersResult = null; try { var ResourceResult = clientFhir.WholeSystemOperation(OperationName, ParametersIn); ParametersResult = ResourceResult as Parameters; } catch (Exception Exec) { Assert.True(false, $"Exception thrown on Operation call to ${OperationName}: " + Exec.Message); } Assert.NotNull(ParametersResult, "Resource create by Updated returned resource of null"); Assert.NotNull(ParametersResult.Parameter, "ParametersResult.Parameter is null"); Assert.AreEqual(ParametersResult.Parameter.Count(), 1, "ParametersResult.Parameter contains more than one parameter."); Assert.AreEqual(ParametersResult.Parameter[0].Name, $"{FHIRAllTypes.Patient.GetLiteral()}_TotalIndexesDeletedCount", "ParametersResult.Parameter.Name not as expected."); Assert.IsInstanceOf <FhirDecimal>(ParametersResult.Parameter[0].Value, "ParametersResult.Parameter.Value expected FhirDecimal."); Assert.Greater((ParametersResult.Parameter[0].Value as FhirDecimal).Value, 0, "ParametersResult.Parameter.Value expected to be greater than 0."); ParametersResult = null; //------------------------------------------------------------------------------------ // ------------ Resource Base Operation ALL resource ResourceType = *---------------------------------------------- //------------------------------------------------------------------------------------ //Now setup to use the base operation $delete-history-indexes //Parameter Resource ParametersIn = new Parameters(); ParametersIn.Id = Guid.NewGuid().ToString(); ParametersIn.Parameter = new List <Parameters.ParameterComponent>(); ParamOne = new Parameters.ParameterComponent(); ParametersIn.Parameter.Add(ParamOne); ParamOne.Name = "ResourceType"; ParamOne.Value = new FhirString("*"); ParametersResult = null; try { var ResourceResult = clientFhir.WholeSystemOperation(OperationName, ParametersIn); ParametersResult = ResourceResult as Parameters; } catch (Exception Exec) { Assert.True(false, $"Exception thrown on Operation call to ${OperationName}: " + Exec.Message); } Assert.NotNull(ParametersResult, "Resource create by Updated returned resource of null"); Assert.NotNull(ParametersResult.Parameter, "ParametersResult.Parameter is null"); Assert.AreEqual(ParametersResult.Parameter.Count(), ModelInfo.SupportedResources.Count, "ParametersResult.Parameter.Count Not equal to Supported resource total."); ParametersResult = null; //------------------------------------------------------------------------------------ // ------------ Resource Type Operation ---------------------------------------------- //------------------------------------------------------------------------------------ //Update the patient again to ensure there are History indexes to delete PatientResult = null; try { PatientResult = clientFhir.Update <Patient>(PatientOne); } catch (Exception Exec) { Assert.True(false, "Exception thrown on Patient resource Update: " + Exec.Message); } Assert.NotNull(PatientResult, "Resource create by Updated returned resource of null"); ParametersIn = new Parameters(); ParametersIn.Id = Guid.NewGuid().ToString(); ParametersResult = null; try { var ResourceResult = clientFhir.TypeOperation <Patient>(OperationName, ParametersIn); ParametersResult = ResourceResult as Parameters; } catch (Exception Exec) { Assert.True(false, $"Exception thrown on Operation call to ${OperationName}: " + Exec.Message); } Assert.NotNull(ParametersResult, "Resource create by Updated returned resource of null"); Assert.NotNull(ParametersResult.Parameter, "ParametersResult.Parameter is null"); Assert.AreEqual(ParametersResult.Parameter.Count(), 1, "ParametersResult.Parameter contains more than one parameter."); Assert.AreEqual(ParametersResult.Parameter[0].Name, $"{FHIRAllTypes.Patient.GetLiteral()}_TotalIndexesDeletedCount", "ParametersResult.Parameter.Name not as expected."); Assert.IsInstanceOf <FhirDecimal>(ParametersResult.Parameter[0].Value, "ParametersResult.Parameter.Value expected FhirDecimal."); Assert.Greater((ParametersResult.Parameter[0].Value as FhirDecimal).Value, 0, "ParametersResult.Parameter.Value expected to be greater than 0."); //--- Clean Up --------------------------------------------------------- //Clean up by deleting all Test Patients SearchParams sp = new SearchParams().Where($"identifier={StaticTestData.TestIdentiferSystem}|"); try { clientFhir.Delete("Patient", sp); } catch (Exception Exec) { Assert.True(false, "Exception thrown on conditional delete of resource Patient: " + Exec.Message); } }
public async Task GivenASearchParameterWithMultipleBaseResourceTypes_WhenTargetingReindexJobToSameListOfResourceTypes_ThenSearchParametersMarkedFullyIndexed() { var randomName = Guid.NewGuid().ToString().ComputeHash().Substring(0, 14).ToLower(); var searchParam = Samples.GetJsonSample <SearchParameter>("SearchParameter-Resource-idfoo"); searchParam.Name = searchParam.Name.Replace("foo", randomName); searchParam.Url = searchParam.Url.Replace("foo", randomName); searchParam.Code = randomName + "Code"; searchParam.Base = new List <ResourceType?>() { ResourceType.Appointment, ResourceType.Immunization }; // POST a new appointment var appointment = Samples.GetJsonSample <Appointment>("Appointment"); FhirResponse <Appointment> expectedAppointment = await Client.CreateAsync(appointment); // POST a second appointment to show it is filtered and not returned when using the new search parameter var appointment2 = Samples.GetJsonSample <Appointment>("Appointment"); await Client.CreateAsync(appointment2); // POST a new Immunization var immunization = Samples.GetJsonSample <Immunization>("Immunization"); FhirResponse <Immunization> expectedImmunization = await Client.CreateAsync(immunization); // POST a new Search parameter FhirResponse <SearchParameter> searchParamPosted = null; try { searchParamPosted = await Client.CreateAsync(searchParam); _output.WriteLine($"SearchParameter is posted {searchParam.Url}"); Uri reindexJobUri; // Start a reindex job var reindexParameters = new Parameters(); reindexParameters.Add("targetResourceTypes", new FhirString("Appointment,Immunization")); (_, reindexJobUri) = await Client.PostReindexJobAsync(reindexParameters); await WaitForReindexStatus(reindexJobUri, "Completed"); FhirResponse <Parameters> reindexJobResult = await Client.CheckReindexAsync(reindexJobUri); Parameters.ParameterComponent searchParamListParam = reindexJobResult.Resource.Parameter.FirstOrDefault(p => p.Name == "searchParams"); Parameters.ParameterComponent targetResourcesParam = reindexJobResult.Resource.Parameter.FirstOrDefault(p => p.Name == JobRecordProperties.TargetResourceTypes); Parameters.ParameterComponent resourcesParam = reindexJobResult.Resource.Parameter.FirstOrDefault(p => p.Name == JobRecordProperties.Resources); // output reindex job for debugging _output.WriteLine("ReindexJobDocument:"); var serializer = new FhirJsonSerializer(); _output.WriteLine(serializer.SerializeToString(reindexJobResult.Resource)); Assert.Contains(searchParamPosted.Resource.Url, searchParamListParam?.Value?.ToString()); Assert.Contains("Appointment", targetResourcesParam?.Value?.ToString()); Assert.Contains("Appointment", resourcesParam?.Value?.ToString()); Assert.Contains("Immunization", targetResourcesParam?.Value?.ToString()); Assert.Contains("Immunization", resourcesParam?.Value?.ToString()); _output.WriteLine($"Reindex job is completed, it should have reindexed the resources of type Appointment and Immunization only."); var floatParse = float.TryParse( reindexJobResult.Resource.Parameter.FirstOrDefault(predicate => predicate.Name == "resourcesSuccessfullyReindexed").Value.ToString(), out float resourcesReindexed); _output.WriteLine($"Reindex job is completed, {resourcesReindexed} resources Reindexed"); Assert.True(floatParse); Assert.True(resourcesReindexed > 0.0); // When job complete, search for resources using new parameter // When there are multiple instances of the fhir-server running, it could take some time // for the search parameter/reindex updates to propogate to all instances. Hence we are // adding some retries below to account for that delay. int retryCount = 0; bool success = true; do { success = true; retryCount++; try { await ExecuteAndValidateBundle( $"Appointment?{searchParam.Code}={expectedAppointment.Resource.Id}", expectedAppointment.Resource); await ExecuteAndValidateBundle( $"Immunization?{searchParam.Code}={expectedImmunization.Resource.Id}", expectedImmunization.Resource); } catch (Exception ex) { _output.WriteLine($"Failed to validate bundle: {ex}"); success = false; await Task.Delay(10000); } }while (!success && retryCount < 3); Assert.True(success); } catch (FhirException ex) when(ex.StatusCode == HttpStatusCode.BadRequest && ex.Message.Contains("not enabled")) { _output.WriteLine($"Skipping because reindex is disabled."); Skip.If(!_fixture.IsUsingInProcTestServer, "Reindex is not enabled on this server."); return; } catch (Exception e) { _output.WriteLine($"Exception: {e.Message}"); _output.WriteLine($"Stack Trace: {e.StackTrace}"); throw; } finally { // Clean up new SearchParameter await DeleteSearchParameterAndVerify(searchParamPosted?.Resource); } }