public async Task GivenNoSupportedSearchParameters_WhenRunningReindexJob_ThenJobIsCanceled() { var request = new CreateReindexRequest(); CreateReindexResponse response = await _createReindexRequestHandler.Handle(request, CancellationToken.None); Assert.NotNull(response); Assert.False(string.IsNullOrWhiteSpace(response.Job.JobRecord.Id)); _reindexJobWorker = new ReindexJobWorker( () => _scopedOperationDataStore, Options.Create(_jobConfiguration), InitializeReindexJobTask, _searchParameterOperations, NullLogger <ReindexJobWorker> .Instance); await _reindexJobWorker.Handle(new SearchParametersInitializedNotification(), CancellationToken.None); var cancellationTokenSource = new CancellationTokenSource(); try { await PerformReindexingOperation(response, OperationStatus.Canceled, cancellationTokenSource); } finally { cancellationTokenSource.Cancel(); } }
public async Task GivenNoMatchingResources_WhenRunningReindexJob_ThenJobIsCompleted() { var searchParam = _supportedSearchParameterDefinitionManager.GetSearchParameter(new Uri("http://hl7.org/fhir/SearchParameter/Measure-name")); searchParam.IsSearchable = false; var request = new CreateReindexRequest(); CreateReindexResponse response = await _createReindexRequestHandler.Handle(request, CancellationToken.None); Assert.NotNull(response); Assert.False(string.IsNullOrWhiteSpace(response.Job.JobRecord.Id)); _reindexJobWorker = new ReindexJobWorker( () => _scopedOperationDataStore, Options.Create(_jobConfiguration), InitializeReindexJobTask, _searchParameterOperations, NullLogger <ReindexJobWorker> .Instance); await _reindexJobWorker.Handle(new SearchParametersInitializedNotification(), CancellationToken.None); var cancellationTokenSource = new CancellationTokenSource(); try { await PerformReindexingOperation(response, OperationStatus.Completed, cancellationTokenSource); Assert.True(searchParam.IsSearchable); } finally { cancellationTokenSource.Cancel(); searchParam.IsSearchable = true; } }
public async Task GivenNewSearchParamCreatedAfterResourcesToBeIndexed_WhenReindexJobCompleted_ThenResourcesAreIndexedAndParamIsSearchable() { const string sampleName1 = "searchIndicesPatient1"; const string sampleName2 = "searchIndicesPatient2"; UpsertOutcome sample1 = await CreatePatientResource(sampleName1); UpsertOutcome sample2 = await CreatePatientResource(sampleName2); const string searchParamName = "foo"; const string searchParamCode = "fooCode"; SearchParameter searchParam = await CreateSearchParam(searchParamName, searchParamCode); // Create the query <fhirserver>/Patient?foo=searchIndicesPatient1 var queryParams = new List <Tuple <string, string> >() { new Tuple <string, string>(searchParamCode, sampleName1) }; SearchResult searchResults = await _searchService.Value.SearchAsync("Patient", queryParams, CancellationToken.None); // Confirm that the search parameter "foo" is marked as unsupported Assert.Equal(searchParamCode, searchResults.UnsupportedSearchParameters.FirstOrDefault()?.Item1); // When search parameters aren't recognized, they are ignored // Confirm that "foo" is dropped from the query string and all patients are returned Assert.Equal(2, searchResults.Results.Count()); // Set up the values that the search index extraction should return during reindexing MockSearchIndexExtraction(sampleName1, sampleName2, searchParam); CreateReindexResponse response = await SetUpForReindexing(); var cancellationTokenSource = new CancellationTokenSource(); try { await PerformReindexingOperation(response, OperationStatus.Completed, cancellationTokenSource); // Rerun the same search as above searchResults = await _searchService.Value.SearchAsync("Patient", queryParams, CancellationToken.None); // This time, foo should not be dropped from the query string Assert.Single(searchResults.Results); // The foo search parameter can be used to filter for the first test patient ResourceWrapper patient = searchResults.Results.FirstOrDefault().Resource; Assert.Contains(sampleName1, patient.RawResource.Data); } finally { cancellationTokenSource.Cancel(); _searchParameterDefinitionManager.DeleteSearchParameter(searchParam.ToTypedElement()); await _testHelper.DeleteSearchParameterStatusAsync(searchParam.Url, CancellationToken.None); await _fixture.DataStore.HardDeleteAsync(sample1.Wrapper.ToResourceKey(), CancellationToken.None); await _fixture.DataStore.HardDeleteAsync(sample2.Wrapper.ToResourceKey(), CancellationToken.None); } }
public static async Task <ResourceElement> CreateReindexJobAsync( this IMediator mediator, ushort?maximumConcurrency, uint?maxResourcesPerQuery, int?queryDelay, ushort?targetDataStoreResourcePercentage, string targetResourceTypesString, CancellationToken cancellationToken) { EnsureArg.IsNotNull(mediator, nameof(mediator)); var targetResourceTypes = new List <string>(); if (!string.IsNullOrEmpty(targetResourceTypesString)) { targetResourceTypes.AddRange(targetResourceTypesString.Split(",").Select(s => s.Trim())); foreach (var resourceType in targetResourceTypes) { if (!Enum.TryParse <Hl7.Fhir.Model.ResourceType>(resourceType, out var result)) { throw new RequestNotValidException(string.Format(Resources.ResourceNotSupported, resourceType)); } } } var request = new CreateReindexRequest(targetResourceTypes, maximumConcurrency, maxResourcesPerQuery, queryDelay, targetDataStoreResourcePercentage); CreateReindexResponse response = await mediator.Send(request, cancellationToken); return(response.Job.ToParametersResourceElement()); }
public async Task GivenValidReindexParameter_WhenCreatingAReindexJob_ThenNewJobShouldBeCreated(string jobRecordProperty, int value) { CreateReindexRequest request; switch (jobRecordProperty) { case JobRecordProperties.MaximumConcurrency: request = new CreateReindexRequest(new List <string>(), (ushort?)value); break; case JobRecordProperties.MaximumNumberOfResourcesPerQuery: request = new CreateReindexRequest(new List <string>(), null, (uint?)value); break; case JobRecordProperties.QueryDelayIntervalInMilliseconds: request = new CreateReindexRequest(new List <string>(), null, null, value); break; case JobRecordProperties.TargetDataStoreUsagePercentage: request = new CreateReindexRequest(new List <string>(), null, null, null, (ushort?)value); break; default: request = new CreateReindexRequest(new List <string>() { jobRecordProperty }); break; } CreateReindexResponse response = await _createReindexRequestHandler.Handle(request, CancellationToken.None); Assert.NotNull(response); Assert.False(string.IsNullOrWhiteSpace(response.Job.JobRecord.Id)); }
public async Task GivenLessThanMaximumRunningJobs_WhenCreatingAReindexJob_ThenNewJobShouldBeCreated() { var request = new CreateReindexRequest(new List <string>()); CreateReindexResponse response = await _createReindexRequestHandler.Handle(request, CancellationToken.None); Assert.NotNull(response); Assert.False(string.IsNullOrWhiteSpace(response.Job.JobRecord.Id)); }
public async Task GivenAlreadyRunningJob_WhenCreatingAReindexJob_ThenJobConflictExceptionThrown() { var request = new CreateReindexRequest(); CreateReindexResponse response = await _createReindexRequestHandler.Handle(request, CancellationToken.None); Assert.NotNull(response); Assert.False(string.IsNullOrWhiteSpace(response.Job.JobRecord.Id)); await Assert.ThrowsAsync <JobConflictException>(() => _createReindexRequestHandler.Handle(request, CancellationToken.None)); }
public static async Task <ResourceElement> CreateReindexJobAsync( this IMediator mediator, ushort?maximumConcurrency, CancellationToken cancellationToken) { EnsureArg.IsNotNull(mediator, nameof(mediator)); var request = new CreateReindexRequest(maximumConcurrency); CreateReindexResponse response = await mediator.Send(request, cancellationToken); return(response.Job.ToParametersResourceElement()); }
public async Task GivenOutOfRangeReindexParameter_WhenCreatingAReindexJob_ThenExceptionShouldBeThrown(string jobRecordProperty, int value) { string errorMessage = "Test error message"; try { CreateReindexRequest request; switch (jobRecordProperty) { case JobRecordProperties.MaximumConcurrency: request = new CreateReindexRequest(new List <string>(), (ushort?)value); errorMessage = string.Format(Fhir.Core.Resources.InvalidReIndexParameterValue, jobRecordProperty, ReindexJobRecord.MinMaximumConcurrency, ReindexJobRecord.MaxMaximumConcurrency); break; case JobRecordProperties.MaximumNumberOfResourcesPerQuery: request = new CreateReindexRequest(new List <string>(), null, (uint?)value); errorMessage = string.Format(Fhir.Core.Resources.InvalidReIndexParameterValue, jobRecordProperty, ReindexJobRecord.MinMaximumNumberOfResourcesPerQuery, ReindexJobRecord.MaxMaximumNumberOfResourcesPerQuery); break; case JobRecordProperties.QueryDelayIntervalInMilliseconds: request = new CreateReindexRequest(new List <string>(), null, null, value); errorMessage = string.Format(Fhir.Core.Resources.InvalidReIndexParameterValue, jobRecordProperty, ReindexJobRecord.MinQueryDelayIntervalInMilliseconds, ReindexJobRecord.MaxQueryDelayIntervalInMilliseconds); break; case JobRecordProperties.TargetDataStoreUsagePercentage: request = new CreateReindexRequest(new List <string>(), null, null, null, (ushort?)value); errorMessage = string.Format(Fhir.Core.Resources.InvalidReIndexParameterValue, jobRecordProperty, ReindexJobRecord.MinTargetDataStoreUsagePercentage, ReindexJobRecord.MaxTargetDataStoreUsagePercentage); break; default: request = new CreateReindexRequest(new List <string>() { jobRecordProperty }); errorMessage = $"Resource type 'Foo' is not supported. (Parameter 'type')"; break; } CreateReindexResponse response = await _createReindexRequestHandler.Handle(request, CancellationToken.None); } catch (FhirException fhirExp) { Assert.NotNull(fhirExp); Assert.Equal(errorMessage.ToLower(), fhirExp.Message.ToLower()); } catch (ArgumentException exp) { Assert.Equal(exp.Message.ToLower(), errorMessage.ToLower()); } }
public static async Task <ResourceElement> CreateReindexJobAsync( this IMediator mediator, ushort?maximumConcurrency, uint?maxResourcesPerQuery, int?queryDelay, ushort?targetDataStoreResourcePercentage, CancellationToken cancellationToken) { EnsureArg.IsNotNull(mediator, nameof(mediator)); var request = new CreateReindexRequest(maximumConcurrency, maxResourcesPerQuery, queryDelay, targetDataStoreResourcePercentage); CreateReindexResponse response = await mediator.Send(request, cancellationToken); return(response.Job.ToParametersResourceElement()); }
private async Task <CreateReindexResponse> SetUpForReindexing() { var request = new CreateReindexRequest(); CreateReindexResponse response = await _createReindexRequestHandler.Handle(request, CancellationToken.None); Assert.NotNull(response); Assert.False(string.IsNullOrWhiteSpace(response.Job.JobRecord.Id)); _reindexJobWorker = new ReindexJobWorker( () => _scopedOperationDataStore, Options.Create(_jobConfiguration), InitializeReindexJobTask, NullLogger <ReindexJobWorker> .Instance); return(response); }
private async Task PerformReindexingOperation(CreateReindexResponse response, OperationStatus operationStatus, CancellationTokenSource cancellationTokenSource) { Task reindexWorkerTask = _reindexJobWorker.ExecuteAsync(cancellationTokenSource.Token); ReindexJobWrapper reindexJobWrapper = await _fhirOperationDataStore.GetReindexJobByIdAsync(response.Job.JobRecord.Id, cancellationTokenSource.Token); int delayCount = 0; while (reindexJobWrapper.JobRecord.Status != operationStatus && delayCount < 10) { await Task.Delay(1000); delayCount++; reindexJobWrapper = await _fhirOperationDataStore.GetReindexJobByIdAsync(response.Job.JobRecord.Id, cancellationTokenSource.Token); } Assert.InRange(delayCount, 0, 9); }
public async Task GivenNoMatchingResources_WhenRunningReindexJob_ThenJobIsCompleted() { var searchParam = _supportedSearchParameterDefinitionManager.GetSearchParameter(new Uri("http://hl7.org/fhir/SearchParameter/Measure-name")); searchParam.IsSearchable = false; var request = new CreateReindexRequest(); CreateReindexResponse response = await _createReindexRequestHandler.Handle(request, CancellationToken.None); Assert.NotNull(response); Assert.False(string.IsNullOrWhiteSpace(response.Job.JobRecord.Id)); _reindexJobWorker = new ReindexJobWorker( () => _scopedOperationDataStore, Options.Create(_jobConfiguration), InitializeReindexJobTask, NullLogger <ReindexJobWorker> .Instance); var cancellationTokenSource = new CancellationTokenSource(); try { var reindexWorkerTask = _reindexJobWorker.ExecuteAsync(cancellationTokenSource.Token); var reindexJobWrapper = await _fhirOperationDataStore.GetReindexJobByIdAsync(response.Job.JobRecord.Id, cancellationTokenSource.Token); int delayCount = 0; while (reindexJobWrapper.JobRecord.Status != OperationStatus.Completed && delayCount < 10) { await Task.Delay(1000); delayCount++; reindexJobWrapper = await _fhirOperationDataStore.GetReindexJobByIdAsync(response.Job.JobRecord.Id, cancellationTokenSource.Token); } Assert.True(delayCount <= 9); Assert.True(searchParam.IsSearchable); } finally { cancellationTokenSource.Cancel(); searchParam.IsSearchable = true; } }
private async Task <CreateReindexResponse> SetUpForReindexing(CreateReindexRequest request = null) { if (request == null) { request = new CreateReindexRequest(new List <string>()); } CreateReindexResponse response = await _createReindexRequestHandler.Handle(request, CancellationToken.None); Assert.NotNull(response); Assert.False(string.IsNullOrWhiteSpace(response.Job.JobRecord.Id)); _reindexJobWorker = new ReindexJobWorker( () => _scopedOperationDataStore, Options.Create(_jobConfiguration), InitializeReindexJobTask, _searchParameterOperations, NullLogger <ReindexJobWorker> .Instance); await _reindexJobWorker.Handle(new SearchParametersInitializedNotification(), CancellationToken.None); return(response); }
public async Task GivenNewSearchParam_WhenReindexJobCompleted_ThenParamIsSearchable() { var searchParamName = "foo"; var searchParamCode = "fooCode"; var searchParam = new SearchParameterInfo( name: searchParamName, code: searchParamCode, searchParamType: ValueSets.SearchParamType.String, url: new Uri("http://hl7.org/fhir/SearchParameter/Patient-foo"), components: null, expression: "Patient.name", targetResourceTypes: null, baseResourceTypes: new List <string>() { "Patient" }) { IsSupported = true, IsSearchable = false, }; _searchParameterDefinitionManager.UrlLookup.TryAdd(searchParam.Url, searchParam); _searchParameterDefinitionManager.TypeLookup["Patient"].TryAdd(searchParamCode, searchParam); await UpsertPatientData("searchIndicesPatient1"); await UpsertPatientData("searchIndicesPatient2"); var queryParams = new List <Tuple <string, string> >() { new Tuple <string, string>("fooCode", "searchIndicesPatient1") }; var searchResults = await _searchService.Value.SearchAsync("Patient", queryParams, CancellationToken.None); Assert.Equal(searchParamCode, searchResults.UnsupportedSearchParameters.FirstOrDefault().Item1); Assert.Equal(2, searchResults.Results.Count()); var searchIndexValues1 = new List <SearchIndexEntry>(); searchIndexValues1.Add(new SearchIndexEntry(searchParam, new StringSearchValue("searchIndicesPatient1"))); _searchIndexer.Extract(Arg.Is <ResourceElement>(r => r.Id.Equals("searchIndicesPatient1"))).Returns(searchIndexValues1); var searchIndexValues2 = new List <SearchIndexEntry>(); searchIndexValues2.Add(new SearchIndexEntry(searchParam, new StringSearchValue("searchIndicesPatient2"))); _searchIndexer.Extract(Arg.Is <ResourceElement>(r => r.Id.Equals("searchIndicesPatient2"))).Returns(searchIndexValues2); var request = new CreateReindexRequest(); CreateReindexResponse response = await _createReindexRequestHandler.Handle(request, CancellationToken.None); Assert.NotNull(response); Assert.False(string.IsNullOrWhiteSpace(response.Job.JobRecord.Id)); _reindexJobWorker = new ReindexJobWorker( () => _scopedOperationDataStore, Options.Create(_jobConfiguration), InitializeReindexJobTask, NullLogger <ReindexJobWorker> .Instance); var cancellationTokenSource = new CancellationTokenSource(); try { var reindexWorkerTask = _reindexJobWorker.ExecuteAsync(cancellationTokenSource.Token); var reindexJobWrapper = await _fhirOperationDataStore.GetReindexJobByIdAsync(response.Job.JobRecord.Id, cancellationTokenSource.Token); int delayCount = 0; while (reindexJobWrapper.JobRecord.Status != OperationStatus.Completed && delayCount < 10) { await Task.Delay(1000); delayCount++; reindexJobWrapper = await _fhirOperationDataStore.GetReindexJobByIdAsync(response.Job.JobRecord.Id, cancellationTokenSource.Token); } Assert.True(delayCount <= 9); searchResults = await _searchService.Value.SearchAsync("Patient", queryParams, CancellationToken.None); Assert.Single(searchResults.Results); var patient = searchResults.Results.FirstOrDefault().Resource; Assert.Contains("searchIndicesPatient1", patient.RawResource.Data); } finally { cancellationTokenSource.Cancel(); } }
public async Task GivenReindexJobRunning_WhenReindexJobCancelRequest_ThenReindexJobStopsAndMarkedCanceled() { var randomName = Guid.NewGuid().ToString().ComputeHash().Substring(0, 14).ToLower(); string searchParamName = randomName; string searchParamCode = randomName + "Code"; SearchParameter searchParam = await CreateSearchParam(searchParamName, SearchParamType.String, ResourceType.Patient, "Patient.name", searchParamCode); const string sampleName1 = "searchIndicesPatient1"; const string sampleName2 = "searchIndicesPatient2"; const string sampleName3 = "searchIndicesPatient3"; const string sampleName4 = "searchIndicesPatient4"; string sampleId1 = Guid.NewGuid().ToString(); string sampleId2 = Guid.NewGuid().ToString(); string sampleId3 = Guid.NewGuid().ToString(); string sampleId4 = Guid.NewGuid().ToString(); // Set up the values that the search index extraction should return during reindexing var searchValues = new List <(string, ISearchValue)> { (sampleId1, new StringSearchValue(sampleName1)), (sampleId2, new StringSearchValue(sampleName2)), (sampleId3, new StringSearchValue(sampleName3)), (sampleId4, new StringSearchValue(sampleName4)), }; MockSearchIndexExtraction(searchValues, searchParam); UpsertOutcome sample1 = await CreatePatientResource(sampleName1, sampleId1); UpsertOutcome sample2 = await CreatePatientResource(sampleName2, sampleId2); UpsertOutcome sample3 = await CreatePatientResource(sampleName3, sampleId3); UpsertOutcome sample4 = await CreatePatientResource(sampleName4, sampleId4); // Create the query <fhirserver>/Patient?foo=searchIndicesPatient1 var queryParams = new List <Tuple <string, string> > { new(searchParamCode, sampleName1) }; SearchResult searchResults = await _searchService.Value.SearchAsync("Patient", queryParams, CancellationToken.None); // Confirm that the search parameter "foo" is marked as unsupported Assert.Equal(searchParamCode, searchResults.UnsupportedSearchParameters.FirstOrDefault()?.Item1); // When search parameters aren't recognized, they are ignored // Confirm that "foo" is dropped from the query string and all patients are returned Assert.Equal(4, searchResults.Results.Count()); var createReindexRequest = new CreateReindexRequest(1, 1, 500); CreateReindexResponse response = await SetUpForReindexing(createReindexRequest); var cancellationTokenSource = new CancellationTokenSource(); try { var cancelReindexHandler = new CancelReindexRequestHandler(_fhirOperationDataStore, DisabledFhirAuthorizationService.Instance); Task reindexWorkerTask = _reindexJobWorker.ExecuteAsync(cancellationTokenSource.Token); await cancelReindexHandler.Handle(new CancelReindexRequest(response.Job.JobRecord.Id), CancellationToken.None); var reindexWrapper = await _fhirOperationDataStore.GetReindexJobByIdAsync(response.Job.JobRecord.Id, cancellationTokenSource.Token); Assert.Equal(OperationStatus.Canceled, reindexWrapper.JobRecord.Status); } catch (RequestNotValidException ex) { // Despite the settings above of the create reindex request which processes only one resource // every 500ms, sometimes when the test runs the reindex job is completed before the // the cancellation request is processed. We will ignore this error if (!ex.Message.Contains("in state Completed and cannot be cancelled", StringComparison.OrdinalIgnoreCase)) { throw; } } finally { cancellationTokenSource.Cancel(); _searchParameterDefinitionManager.DeleteSearchParameter(searchParam.ToTypedElement()); await _testHelper.DeleteSearchParameterStatusAsync(searchParam.Url, CancellationToken.None); await _fixture.DataStore.HardDeleteAsync(sample1.Wrapper.ToResourceKey(), CancellationToken.None); await _fixture.DataStore.HardDeleteAsync(sample2.Wrapper.ToResourceKey(), CancellationToken.None); await _fixture.DataStore.HardDeleteAsync(sample3.Wrapper.ToResourceKey(), CancellationToken.None); await _fixture.DataStore.HardDeleteAsync(sample4.Wrapper.ToResourceKey(), CancellationToken.None); } }
public async Task GivenNewSearchParamCreatedBeforeResourcesToBeIndexed_WhenReindexJobCompleted_ThenResourcesAreIndexedAndParamIsSearchable() { var randomName = Guid.NewGuid().ToString().ComputeHash().Substring(0, 14).ToLower(); string searchParamName = randomName; string searchParamCode = randomName + "Code"; SearchParameter searchParam = await CreateSearchParam(searchParamName, SearchParamType.String, ResourceType.Patient, "Patient.name", searchParamCode); string sampleName1 = randomName + "searchIndicesPatient1"; string sampleName2 = randomName + "searchIndicesPatient2"; string sampleId1 = Guid.NewGuid().ToString(); string sampleId2 = Guid.NewGuid().ToString(); // Set up the values that the search index extraction should return during reindexing var searchValues = new List <(string, ISearchValue)> { (sampleId1, new StringSearchValue(sampleName1)), (sampleId2, new StringSearchValue(sampleName2)) }; MockSearchIndexExtraction(searchValues, searchParam); UpsertOutcome sample1 = await CreatePatientResource(sampleName1, sampleId1); UpsertOutcome sample2 = await CreatePatientResource(sampleName2, sampleId2); // Create the query <fhirserver>/Patient?foo=searchIndicesPatient1 var queryParams = new List <Tuple <string, string> > { new(searchParamCode, sampleName1) }; SearchResult searchResults = await _searchService.Value.SearchAsync("Patient", queryParams, CancellationToken.None); // Confirm that the search parameter "foo" is marked as unsupported Assert.Equal(searchParamCode, searchResults.UnsupportedSearchParameters.FirstOrDefault()?.Item1); // When search parameters aren't recognized, they are ignored // Confirm that "foo" is dropped from the query string and all patients are returned Assert.Equal(2, searchResults.Results.Count()); CreateReindexResponse response = await SetUpForReindexing(); var cancellationTokenSource = new CancellationTokenSource(); try { await PerformReindexingOperation(response, OperationStatus.Completed, cancellationTokenSource); // Rerun the same search as above searchResults = await _searchService.Value.SearchAsync("Patient", queryParams, CancellationToken.None); // This time, foo should not be dropped from the query string Assert.Single(searchResults.Results); // The foo search parameter can be used to filter for the first test patient ResourceWrapper patient = searchResults.Results.FirstOrDefault().Resource; Assert.Contains(sampleName1, patient.RawResource.Data); // Confirm that the reindexing operation did not create a new version of the resource Assert.Equal("1", searchResults.Results.FirstOrDefault().Resource.Version); } finally { cancellationTokenSource.Cancel(); _searchParameterDefinitionManager.DeleteSearchParameter(searchParam.ToTypedElement()); await _testHelper.DeleteSearchParameterStatusAsync(searchParam.Url, CancellationToken.None); await _fixture.DataStore.HardDeleteAsync(sample1.Wrapper.ToResourceKey(), false, CancellationToken.None); await _fixture.DataStore.HardDeleteAsync(sample2.Wrapper.ToResourceKey(), false, CancellationToken.None); } }
public async Task GivenNewSearchParamWithResourceBaseType_WhenReindexJobCompleted_ThenAllResourcesAreIndexedAndParamIsSearchable() { string patientId = Guid.NewGuid().ToString(); string observationId = Guid.NewGuid().ToString(); UpsertOutcome samplePatient = await CreatePatientResource("samplePatient", patientId); UpsertOutcome sampleObservation = await CreateObservationResource(observationId); const string searchParamName = "resourceFoo"; const string searchParamCode = "resourceFooCode"; SearchParameter searchParam = await CreateSearchParam(searchParamName, SearchParamType.Token, ResourceType.Resource, "Resource.id", searchParamCode); // Create the query <fhirserver>/Patient?resourceFooCode=<patientId> var queryParams = new List <Tuple <string, string> > { new(searchParamCode, patientId) }; SearchResult searchResults = await _searchService.Value.SearchAsync("Patient", queryParams, CancellationToken.None); // Confirm that the search parameter "resourceFoo" is marked as unsupported Assert.Equal(searchParamCode, searchResults.UnsupportedSearchParameters.FirstOrDefault()?.Item1); // Set up the values that the search index extraction should return during reindexing var searchValues = new List <(string, ISearchValue)> { (patientId, new TokenSearchValue(null, patientId, null)), (observationId, new TokenSearchValue(null, observationId, null)) }; MockSearchIndexExtraction(searchValues, searchParam); CreateReindexResponse response = await SetUpForReindexing(); var cancellationTokenSource = new CancellationTokenSource(); try { await PerformReindexingOperation(response, OperationStatus.Completed, cancellationTokenSource); // Rerun the same search as above searchResults = await _searchService.Value.SearchAsync("Patient", queryParams, CancellationToken.None); Assert.Single(searchResults.Results); // Confirm that the search parameter "resourceFoo" isn't marked as unsupported Assert.DoesNotContain(searchResults.UnsupportedSearchParameters, t => t.Item1 == searchParamCode); // Create the query <fhirserver>/Patient?resourceFooCode=<nonexistent-id> queryParams = new List <Tuple <string, string> > { new(searchParamCode, "nonexistent-id") }; // No resources should be returned searchResults = await _searchService.Value.SearchAsync("Patient", queryParams, CancellationToken.None); Assert.Empty(searchResults.Results); // Create the query <fhirserver>/Observation?resourceFooCode=<observationId> queryParams = new List <Tuple <string, string> > { new(searchParamCode, observationId) }; // Check that the new search parameter can be used with a different type of resource searchResults = await _searchService.Value.SearchAsync("Observation", queryParams, CancellationToken.None); Assert.Single(searchResults.Results); // Confirm that the search parameter "resourceFoo" isn't marked as unsupported Assert.DoesNotContain(searchResults.UnsupportedSearchParameters, t => t.Item1 == searchParamCode); // Create the query <fhirserver>/Observation?resourceFooCode=<nonexistent-id> queryParams = new List <Tuple <string, string> > { new(searchParamCode, "nonexistent-id") }; // No resources should be returned searchResults = await _searchService.Value.SearchAsync("Observation", queryParams, CancellationToken.None); Assert.Empty(searchResults.Results); } finally { cancellationTokenSource.Cancel(); _searchParameterDefinitionManager.DeleteSearchParameter(searchParam.ToTypedElement()); await _testHelper.DeleteSearchParameterStatusAsync(searchParam.Url, CancellationToken.None); await _fixture.DataStore.HardDeleteAsync(samplePatient.Wrapper.ToResourceKey(), CancellationToken.None); await _fixture.DataStore.HardDeleteAsync(sampleObservation.Wrapper.ToResourceKey(), CancellationToken.None); } }