Example #1
0
 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)));
        }
Example #3
0
        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);
            }
        }
Example #7
0
        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)));
        }
Example #10
0
        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)));
        }
Example #11
0
        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)));
        }
Example #13
0
        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);
            }
        }
Example #14
0
        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);
            }
        }
Example #15
0
        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);
            }
        }