public UpsertResponse(T controlPlaneResource, UpsertOutcome outcomeType, string eTag) { EnsureArg.IsNotNull(controlPlaneResource, nameof(controlPlaneResource)); Resource = controlPlaneResource; OutcomeType = outcomeType; ETag = eTag; }
public async Task <UpsertResourceResponse> Handle(UpsertResourceRequest message, CancellationToken cancellationToken) { EnsureArg.IsNotNull(message, nameof(message)); if (await AuthorizationService.CheckAccess(DataActions.Write) != DataActions.Write) { throw new UnauthorizedFhirActionException(); } Resource resource = message.Resource.ToPoco <Resource>(); if (await ConformanceProvider.Value.RequireETag(resource.TypeName, cancellationToken) && message.WeakETag == null) { throw new PreconditionFailedException(string.Format(Core.Resources.IfMatchHeaderRequiredForResource, resource.TypeName)); } bool allowCreate = await ConformanceProvider.Value.CanUpdateCreate(resource.TypeName, cancellationToken); bool keepHistory = await ConformanceProvider.Value.CanKeepHistory(resource.TypeName, cancellationToken); ResourceWrapper resourceWrapper = CreateResourceWrapper(resource, deleted: false, keepMeta: allowCreate); UpsertOutcome result = await UpsertAsync(message, resourceWrapper, allowCreate, keepHistory, cancellationToken); resource.VersionId = result.Wrapper.Version; return(new UpsertResourceResponse(new SaveOutcome(new RawResourceElement(result.Wrapper), result.OutcomeType))); }
public async Task GivenAnUpdatedResourceWithWrongWeakETag_WhenUpdateSearchIndexForResourceAsync_ThenExceptionIsThrown() { ResourceElement patientResource = Samples.GetJsonSample("Patient"); SaveOutcome upsertResult = await Mediator.UpsertResourceAsync(patientResource); (ResourceWrapper originalWrapper, ResourceWrapper updatedWrapper) = await CreateUpdatedWrapperFromExistingResource(upsertResult); UpsertOutcome upsertOutcome = await _dataStore.UpsertAsync(updatedWrapper, WeakETag.FromVersionId(originalWrapper.Version), allowCreate : false, keepHistory : false, CancellationToken.None); // Let's update the resource again with new information. var searchParamInfo = new SearchParameterInfo("newSearchParam2"); var searchIndex = new SearchIndexEntry(searchParamInfo, new TokenSearchValue("system", "code", "text")); var searchIndices = new List <SearchIndexEntry>() { searchIndex }; updatedWrapper = new ResourceWrapper( originalWrapper.ResourceId, originalWrapper.Version, originalWrapper.ResourceTypeName, originalWrapper.RawResource, originalWrapper.Request, originalWrapper.LastModified, deleted: false, searchIndices, originalWrapper.CompartmentIndices, originalWrapper.LastModifiedClaims); // Attempt to replace resource with the old weaketag await Assert.ThrowsAsync <PreconditionFailedException>(() => _dataStore.UpdateSearchIndexForResourceAsync(updatedWrapper, WeakETag.FromVersionId(originalWrapper.Version), CancellationToken.None)); }
public async Task GivenResourceWithDifferentHash_WhenPerformingReindexSearch_ThenResourceShouldBeReturned() { ResourceWrapper testPatient = null; try { UpsertOutcome outcome = await UpsertPatientData(); testPatient = outcome.Wrapper; var queryParametersList = new List <Tuple <string, string> >() { Tuple.Create(KnownQueryParameterNames.Count, "100"), Tuple.Create(KnownQueryParameterNames.Type, "Patient"), }; // Pass in a different hash value SearchResult searchResult = await _searchService.Value.SearchForReindexAsync(queryParametersList, "differentHash", false, CancellationToken.None); // A reindex search should return all the resources that have a different hash value than the one specified. Assert.Single(searchResult.Results); } finally { if (testPatient != null) { await _scopedDataStore.Value.HardDeleteAsync(testPatient.ToResourceKey(), false, CancellationToken.None); } } }
public async Task <UpsertResourceResponse> Handle(UpsertResourceRequest request, CancellationToken cancellationToken) { EnsureArg.IsNotNull(request, nameof(request)); if (await AuthorizationService.CheckAccess(DataActions.Write, cancellationToken) != DataActions.Write) { throw new UnauthorizedFhirActionException(); } Resource resource = request.Resource.ToPoco <Resource>(); bool allowCreate = await ConformanceProvider.Value.CanUpdateCreate(resource.TypeName, cancellationToken); bool keepHistory = await ConformanceProvider.Value.CanKeepHistory(resource.TypeName, cancellationToken); bool requireETagOnUpdate = await ConformanceProvider.Value.RequireETag(resource.TypeName, cancellationToken); ResourceWrapper resourceWrapper = CreateResourceWrapper(resource, deleted: false, keepMeta: allowCreate); UpsertOutcome result = await FhirDataStore.UpsertAsync(resourceWrapper, request.WeakETag, allowCreate, keepHistory, cancellationToken, requireETagOnUpdate); resource.VersionId = result.Wrapper.Version; return(new UpsertResourceResponse(new SaveOutcome(new RawResourceElement(result.Wrapper), result.OutcomeType))); }
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 async Task <DeleteResourceResponse> Handle(DeleteResourceRequest request, CancellationToken cancellationToken) { EnsureArg.IsNotNull(request, nameof(request)); DataActions requiredDataAction = request.DeleteOperation == DeleteOperation.SoftDelete ? DataActions.Delete : DataActions.HardDelete | DataActions.Delete; if (await AuthorizationService.CheckAccess(requiredDataAction, cancellationToken) != requiredDataAction) { throw new UnauthorizedFhirActionException(); } var key = request.ResourceKey; if (!string.IsNullOrEmpty(key.VersionId)) { throw new MethodNotAllowedException(Core.Resources.DeleteVersionNotAllowed); } string version = null; switch (request.DeleteOperation) { case DeleteOperation.SoftDelete: var emptyInstance = (Resource)Activator.CreateInstance(ModelInfo.GetTypeForFhirType(request.ResourceKey.ResourceType)); emptyInstance.Id = request.ResourceKey.Id; ResourceWrapper deletedWrapper = CreateResourceWrapper(emptyInstance, deleted: true, keepMeta: false); bool keepHistory = await ConformanceProvider.Value.CanKeepHistory(key.ResourceType, cancellationToken); UpsertOutcome result = await FhirDataStore.UpsertAsync( deletedWrapper, weakETag : null, allowCreate : true, keepHistory : keepHistory, cancellationToken : cancellationToken); version = result?.Wrapper.Version; break; case DeleteOperation.HardDelete: case DeleteOperation.PurgeHistory: await FhirDataStore.HardDeleteAsync(key, request.DeleteOperation == DeleteOperation.PurgeHistory, cancellationToken); break; default: throw new ArgumentOutOfRangeException(nameof(request)); } if (string.IsNullOrWhiteSpace(version)) { return(new DeleteResourceResponse(new ResourceKey(key.ResourceType, key.Id))); } return(new DeleteResourceResponse(new ResourceKey(key.ResourceType, key.Id, version), weakETag: WeakETag.FromVersionId(version))); }
public async Task <DeleteResourceResponse> Handle(DeleteResourceRequest message, CancellationToken cancellationToken) { EnsureArg.IsNotNull(message, nameof(message)); var key = message.ResourceKey; if (!string.IsNullOrEmpty(key.VersionId)) { throw new MethodNotAllowedException(Core.Resources.DeleteVersionNotAllowed); } string version = null; if (message.HardDelete) { await FhirDataStore.HardDeleteAsync(key, cancellationToken); } else { ResourceWrapper existing = await FhirDataStore.GetAsync(key, cancellationToken); version = existing?.Version; if (existing?.IsDeleted == false) { var emptyInstance = (Resource)Activator.CreateInstance(ModelInfo.GetTypeForFhirType(existing.ResourceTypeName)); emptyInstance.Id = existing.ResourceId; ResourceWrapper deletedWrapper = CreateResourceWrapper(emptyInstance, deleted: true); bool keepHistory = await ConformanceProvider.Value.CanKeepHistory(key.ResourceType, cancellationToken); UpsertOutcome result = await FhirDataStore.UpsertAsync( deletedWrapper, WeakETag.FromVersionId(existing.Version), allowCreate : true, keepHistory : keepHistory, cancellationToken : cancellationToken); version = result.Wrapper.Version; } } if (string.IsNullOrWhiteSpace(version)) { return(new DeleteResourceResponse(new ResourceKey(key.ResourceType, key.Id))); } return(new DeleteResourceResponse(new ResourceKey(key.ResourceType, key.Id, version), WeakETag.FromVersionId(version))); }
public async Task <DeleteResourceResponse> Handle(DeleteResourceRequest message, CancellationToken cancellationToken) { EnsureArg.IsNotNull(message, nameof(message)); DataActions requiredDataAction = message.HardDelete ? DataActions.Delete | DataActions.HardDelete : DataActions.Delete; if (AuthorizationService.CheckAccess(requiredDataAction) != requiredDataAction) { throw new UnauthorizedFhirActionException(); } var key = message.ResourceKey; if (!string.IsNullOrEmpty(key.VersionId)) { throw new MethodNotAllowedException(Core.Resources.DeleteVersionNotAllowed); } string version = null; if (message.HardDelete) { await FhirDataStore.HardDeleteAsync(key, cancellationToken); } else { var emptyInstance = (Resource)Activator.CreateInstance(ModelInfo.GetTypeForFhirType(message.ResourceKey.ResourceType)); emptyInstance.Id = message.ResourceKey.Id; ResourceWrapper deletedWrapper = CreateResourceWrapper(emptyInstance, deleted: true); bool keepHistory = await ConformanceProvider.Value.CanKeepHistory(key.ResourceType, cancellationToken); UpsertOutcome result = await FhirDataStore.UpsertAsync( deletedWrapper, weakETag : null, allowCreate : true, keepHistory : keepHistory, cancellationToken : cancellationToken); version = result?.Wrapper.Version; } if (string.IsNullOrWhiteSpace(version)) { return(new DeleteResourceResponse(new ResourceKey(key.ResourceType, key.Id))); } return(new DeleteResourceResponse(new ResourceKey(key.ResourceType, key.Id, version), WeakETag.FromVersionId(version))); }
public async Task <PatchResourceResponse> Handle(PatchResourceRequest message, CancellationToken cancellationToken) { EnsureArg.IsNotNull(message, nameof(message)); if (await AuthorizationService.CheckAccess(DataActions.Write, cancellationToken) != DataActions.Write) { throw new UnauthorizedFhirActionException(); } var key = message.ResourceKey; if (!string.IsNullOrEmpty(key.VersionId)) { throw new MethodNotAllowedException(Core.Resources.DeleteVersionNotAllowed); } ResourceWrapper currentDoc = await FhirDataStore.GetAsync(key, cancellationToken); if (currentDoc == null) { throw new ResourceNotFoundException(string.Format(Core.Resources.ResourceNotFoundById, key.ResourceType, key.Id)); } ResourceElement resource = _resourceDeserializer.Deserialize(currentDoc); Resource resourceInstance = resource.Instance.ToPoco <Resource>(); message.PatchDocument.ApplyTo(resourceInstance); ResourceWrapper resourceWrapper = CreateResourceWrapper(resourceInstance, deleted: false, keepMeta: true); bool keepHistory = await ConformanceProvider.Value.CanKeepHistory(currentDoc.ResourceTypeName, cancellationToken); UpsertOutcome result = await FhirDataStore.UpsertAsync( resourceWrapper, weakETag : message.WeakETag, allowCreate : false, keepHistory : keepHistory, cancellationToken : cancellationToken); resourceInstance.VersionId = result.Wrapper.Version; return(new PatchResourceResponse(new SaveOutcome(new RawResourceElement(result.Wrapper), result.OutcomeType))); }
public async Task <UpsertResourceResponse> Handle(UpsertResourceRequest message, CancellationToken cancellationToken) { EnsureArg.IsNotNull(message, nameof(message)); Resource resource = message.Resource; if (await ConformanceProvider.Value.RequireETag(resource.TypeName, cancellationToken) && message.WeakETag == null) { throw new PreconditionFailedException(string.Format(Core.Resources.IfMatchHeaderRequiredForResource, resource.TypeName)); } bool allowCreate = await ConformanceProvider.Value.CanUpdateCreate(resource.TypeName, cancellationToken); bool keepHistory = await ConformanceProvider.Value.CanKeepHistory(resource.TypeName, cancellationToken); ResourceWrapper resourceWrapper = CreateResourceWrapper(resource, deleted: false); UpsertOutcome result = await FhirDataStore.UpsertAsync(resourceWrapper, message.WeakETag, allowCreate, keepHistory, cancellationToken); resource.VersionId = result.Wrapper.Version; return(new UpsertResourceResponse(new SaveOutcome(resource, result.OutcomeType))); }
public async Task <UpsertResourceResponse> Handle(CreateResourceRequest message, CancellationToken cancellationToken) { EnsureArg.IsNotNull(message, nameof(message)); Resource resource = message.Resource; // If an Id is supplied on create it should be removed/ignored resource.Id = null; ResourceWrapper resourceWrapper = CreateResourceWrapper(resource, deleted: false); bool keepHistory = await ConformanceProvider.Value.CanKeepHistory(resource.TypeName, cancellationToken); UpsertOutcome result = await DataStore.UpsertAsync( resourceWrapper, weakETag : null, allowCreate : true, keepHistory : keepHistory, cancellationToken : cancellationToken); resource.VersionId = result.Wrapper.Version; return(new UpsertResourceResponse(new SaveOutcome(resource, SaveOutcomeType.Created))); }
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); } }
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); } }