public async Task GivenResourceDoesNotExist_WhenHandle_ThenResourceNotFoundExceptionIsThrown() { _fhirDataStore.GetAsync(Arg.Any <ResourceKey>(), _cancellationToken).Returns(Task.FromResult <ResourceWrapper>(null)); var request = GetReindexRequest(HttpGetName); await Assert.ThrowsAsync <ResourceNotFoundException>(() => _reindexHandler.Handle(request, _cancellationToken)); }
public async Task GivenAnUpdatedResource_WhenUpdatingSearchParameterIndexAsync_ThenResourceMetadataIsUnchanged() { ResourceElement patientResource = CreatePatientResourceElement("Patient", Guid.NewGuid().ToString()); SaveOutcome upsertResult = await Mediator.UpsertResourceAsync(patientResource); SearchParameter searchParam = null; const string searchParamName = "newSearchParam"; try { searchParam = await CreatePatientSearchParam(searchParamName, SearchParamType.String, "Patient.name"); ISearchValue searchValue = new StringSearchValue(searchParamName); (ResourceWrapper original, ResourceWrapper updated) = await CreateUpdatedWrapperFromExistingPatient(upsertResult, searchParam, searchValue); await _dataStore.UpdateSearchParameterIndicesAsync(updated, WeakETag.FromVersionId(original.Version), CancellationToken.None); // Get the reindexed resource from the database var resourceKey1 = new ResourceKey(upsertResult.RawResourceElement.InstanceType, upsertResult.RawResourceElement.Id, upsertResult.RawResourceElement.VersionId); ResourceWrapper reindexed = await _dataStore.GetAsync(resourceKey1, CancellationToken.None); VerifyReindexedResource(original, reindexed); } finally { if (searchParam != null) { _searchParameterDefinitionManager.DeleteSearchParameter(searchParam.ToTypedElement()); await _fixture.TestHelper.DeleteSearchParameterStatusAsync(searchParam.Url, CancellationToken.None); } } }
public async Task GivenAFhirMediator_GettingAnResourceThatIsDeleted_ThenAGoneExceptionIsThrown() { var observation = Samples.GetDefaultObservation() .UpdateId("id1"); _fhirDataStore.GetAsync(Arg.Is <ResourceKey>(x => x.Id == "id1"), Arg.Any <CancellationToken>()).Returns(CreateResourceWrapper(observation, true)); await Assert.ThrowsAsync <ResourceGoneException>(async() => await _mediator.GetResourceAsync(new ResourceKey <Observation>("id1"))); await _fhirDataStore.Received().GetAsync(Arg.Any <ResourceKey>(), Arg.Any <CancellationToken>()); }
public async Task <UpsertResourceResponse> Handle(UpsertResourceRequest request, CancellationToken cancellationToken, RequestHandlerDelegate <UpsertResourceResponse> next) { // if the resource type being updated is a SearchParameter, then we want to query the previous version before it is changed // because we will need to the Url property to update the definition in the SearchParameterDefinitionManager // and the user could be changing the Url as part of this update if (request.Resource.InstanceType.Equals(KnownResourceTypes.SearchParameter, StringComparison.Ordinal)) { var resourceKey = new ResourceKey(request.Resource.InstanceType, request.Resource.Id, request.Resource.VersionId); ResourceWrapper prevSearchParamResource = await _fhirDataStore.GetAsync(resourceKey, cancellationToken); if (prevSearchParamResource != null) { // Update the SearchParameterDefinitionManager with the new SearchParameter in order to validate any changes // to the fhirpath or the datatype await _searchParameterOperations.UpdateSearchParameterAsync(request.Resource.Instance, prevSearchParamResource.RawResource, cancellationToken); } else { await _searchParameterOperations.AddSearchParameterAsync(request.Resource.Instance, cancellationToken); } } // Now allow the resource to updated per the normal behavior return(await next()); }
public ListSearchBehaviorTests() { _cancellationToken = _cancellationTokenSource.Token; var so = new SearchOptions(); so.UnsupportedSearchParams = new Tuple <string, string> [0]; _searchOptionsFactory = Substitute.For <ISearchOptionsFactory>(); _searchOptionsFactory.Create(Arg.Any <string>(), Arg.Any <IReadOnlyList <Tuple <string, string> > >()).Returns(so); _fhirDataStore = Substitute.For <IFhirDataStore>(); // for an 'existing list' return a list with Patients _fhirDataStore.GetAsync(Arg.Is <ResourceKey>(x => x.Id == "existing-list"), Arg.Any <CancellationToken>()).Returns( x => { var longList = Samples.GetDefaultList(); var rawResourceFactory = new RawResourceFactory(new FhirJsonSerializer()); return(new ResourceWrapper( longList, rawResourceFactory.Create(longList, keepMeta: true), new ResourceRequest(HttpMethod.Post, "http://fhir"), false, null, null, null)); }); _scopedDataStore = Substitute.For <IScoped <IFhirDataStore> >(); _scopedDataStore.Value.Returns(_fhirDataStore); _nonEmptyBundle = new Bundle { Type = Bundle.BundleType.Batch, Entry = new List <Bundle.EntryComponent> { new Bundle.EntryComponent { Resource = Samples.GetDefaultObservation().ToPoco(), Request = new Bundle.RequestComponent { Method = Bundle.HTTPVerb.POST, Url = "Observation", }, }, new Bundle.EntryComponent { Request = new Bundle.RequestComponent { Method = Bundle.HTTPVerb.GET, Url = "Patient?name=peter", }, }, }, }.ToResourceElement(); _bundleFactory = Substitute.For <IBundleFactory>(); _bundleFactory.CreateSearchBundle(Arg.Any <SearchResult>()).Returns(_nonEmptyBundle); }
public async Task <UpsertResourceResponse> Handle(UpsertResourceRequest request, CancellationToken cancellationToken, RequestHandlerDelegate <UpsertResourceResponse> next) { ResourceWrapper prevSearchParamResource = null; bool isSearchParameter = false; // if the resource type being updated is a SearchParaemter, then we want to query the previous version before it is changed // because we will need to the Url property to update the definition in the SearchParameterDefinitionManager // and the user could be changing the Url as part of this update if (request.Resource.InstanceType.Equals(KnownResourceTypes.SearchParameter, StringComparison.Ordinal)) { isSearchParameter = true; var resourceKey = new ResourceKey(request.Resource.InstanceType, request.Resource.Id, request.Resource.VersionId); prevSearchParamResource = await _fhirDataStore.GetAsync(resourceKey, cancellationToken); } // Now allow the resource to updated per the normal behavior var response = await next(); if (isSearchParameter) { // Once the SearchParameter resource is update in the data store, we will update // the metadata in the SearchParameterDefinitionManager await _searchParameterOperations.UpdateSearchParameterAsync(request.Resource.Instance, prevSearchParamResource.RawResource); } return(response); }
public async Task GivenADeleteResourceRequest_WhenDeletingAResourceOtherThanSearchParameter_ThenNoCallToDeleteParameterMade() { var resource = Samples.GetDefaultObservation().UpdateId("id1"); var key = new ResourceKey("Observation", "id1"); var request = new DeleteResourceRequest(key, DeleteOperation.SoftDelete); var wrapper = CreateResourceWrapper(resource, false); _fhirDataStore.GetAsync(key, Arg.Any <CancellationToken>()).Returns(wrapper); var response = new DeleteResourceResponse(key); var behavior = new DeleteSearchParameterBehavior <DeleteResourceRequest, DeleteResourceResponse>(_searchParameterOperations, _fhirDataStore); await behavior.Handle(request, CancellationToken.None, async() => await Task.Run(() => response)); // Ensure for non-SearchParameter, that we do not call Add SearchParameter await _searchParameterOperations.DidNotReceive().DeleteSearchParameterAsync(Arg.Any <RawResource>()); }
public async Task <ReindexSingleResourceResponse> Handle(ReindexSingleResourceRequest request, CancellationToken cancellationToken) { EnsureArg.IsNotNull(request, nameof(request)); if (await _authorizationService.CheckAccess(DataActions.Reindex) != DataActions.Reindex) { throw new UnauthorizedFhirActionException(); } var key = new ResourceKey(request.ResourceType, request.ResourceId); ResourceWrapper storedResource = await _fhirDataStore.GetAsync(key, cancellationToken); if (storedResource == null) { throw new ResourceNotFoundException(string.Format(Core.Resources.ResourceNotFoundById, request.ResourceType, request.ResourceId)); } // We need to extract the "new" search indices since the assumption is that // a new search parameter has been added to the fhir server. ResourceElement resourceElement = _resourceDeserializer.Deserialize(storedResource); IReadOnlyCollection <SearchIndexEntry> newIndices = _searchIndexer.Extract(resourceElement); // Create a new parameter resource and include the new search indices and the corresponding values. var parametersResource = new Parameters { Id = Guid.NewGuid().ToString(), VersionId = "1", Parameter = new List <Parameters.ParameterComponent>(), }; parametersResource.Parameter.Add(new Parameters.ParameterComponent() { Name = "originalResourceId", Value = new FhirString(request.ResourceId) }); parametersResource.Parameter.Add(new Parameters.ParameterComponent() { Name = "originalResourceType", Value = new FhirString(request.ResourceType) }); foreach (SearchIndexEntry searchIndex in newIndices) { parametersResource.Parameter.Add(new Parameters.ParameterComponent() { Name = searchIndex.SearchParameter.Name.ToString(), Value = new FhirString(searchIndex.Value.ToString()) }); } return(new ReindexSingleResourceResponse(parametersResource.ToResourceElement())); }
public async Task <TDeleteResourceResponse> Handle(TDeleteResourceRequest request, CancellationToken cancellationToken, RequestHandlerDelegate <TDeleteResourceResponse> next) { var deleteRequest = request as DeleteResourceRequest; ResourceWrapper searchParamResource = null; if (deleteRequest.ResourceKey.ResourceType.Equals(KnownResourceTypes.SearchParameter, StringComparison.Ordinal)) { searchParamResource = await _fhirDataStore.GetAsync(deleteRequest.ResourceKey, cancellationToken); } var response = await next(); if (searchParamResource != null && searchParamResource.IsDeleted == false) { // Once the SearchParameter resource is removed from the data store, we can update the in // memory SearchParameterDefinitionManager, and remove the status metadata from the data store await _searchParameterOperations.DeleteSearchParameterAsync(searchParamResource.RawResource); } return(response); }
public async Task <ResourceElement> SearchHistoryAsync( string resourceType, string resourceId, PartialDateTime at, PartialDateTime since, PartialDateTime before, int?count, string continuationToken, CancellationToken cancellationToken) { var queryParameters = new List <Tuple <string, string> >(); if (at != null) { if (since != null) { // _at and _since cannot be both specified. throw new InvalidSearchOperationException( string.Format( CultureInfo.InvariantCulture, Core.Resources.AtCannotBeSpecifiedWithBeforeOrSince, KnownQueryParameterNames.At, KnownQueryParameterNames.Since)); } if (before != null) { // _at and _since cannot be both specified. throw new InvalidSearchOperationException( string.Format( CultureInfo.InvariantCulture, Core.Resources.AtCannotBeSpecifiedWithBeforeOrSince, KnownQueryParameterNames.At, KnownQueryParameterNames.Before)); } } if (before != null) { var beforeOffset = before.ToDateTimeOffset( defaultMonth: 1, defaultDaySelector: (year, month) => 1, defaultHour: 0, defaultMinute: 0, defaultSecond: 0, defaultFraction: 0.0000000m, defaultUtcOffset: TimeSpan.Zero).ToUniversalTime(); if (beforeOffset.CompareTo(Clock.UtcNow) > 0) { // you cannot specify a value for _before in the future throw new InvalidSearchOperationException( string.Format( CultureInfo.InvariantCulture, Core.Resources.HistoryParameterBeforeCannotBeFuture, KnownQueryParameterNames.Before)); } } bool searchByResourceId = !string.IsNullOrEmpty(resourceId); if (searchByResourceId) { queryParameters.Add(Tuple.Create(SearchParameterNames.Id, resourceId)); } if (!string.IsNullOrEmpty(continuationToken)) { queryParameters.Add(Tuple.Create(KnownQueryParameterNames.ContinuationToken, continuationToken)); } if (at != null) { queryParameters.Add(Tuple.Create(SearchParameterNames.LastUpdated, at.ToString())); } else { if (since != null) { queryParameters.Add(Tuple.Create(SearchParameterNames.LastUpdated, $"ge{since}")); } if (before != null) { queryParameters.Add(Tuple.Create(SearchParameterNames.LastUpdated, $"lt{before}")); } } if (count.HasValue && count > 0) { queryParameters.Add(Tuple.Create(KnownQueryParameterNames.Count, count.ToString())); } SearchOptions searchOptions = !string.IsNullOrEmpty(resourceType) ? _searchOptionsFactory.Create(resourceType, queryParameters) : _searchOptionsFactory.Create(queryParameters); SearchResult searchResult = await SearchHistoryInternalAsync(searchOptions, cancellationToken); // If no results are returned from the _history search // determine if the resource actually exists or if the results were just filtered out. // The 'deleted' state has no effect because history will return deleted resources if (searchByResourceId && searchResult.Results.Any() == false) { var resource = await _fhirDataStore.GetAsync(new ResourceKey(resourceType, resourceId), cancellationToken); if (resource == null) { throw new ResourceNotFoundException(string.Format(Core.Resources.ResourceNotFoundById, resourceType, resourceId)); } } return(_bundleFactory.CreateHistoryBundle( unsupportedSearchParams: null, result: searchResult)); }
private async Task <(ResourceWrapper original, ResourceWrapper updated)> CreateUpdatedWrapperFromExistingResource( SaveOutcome upsertResult, string updatedId = null) { // Get wrapper from data store directly ResourceKey resourceKey = new ResourceKey(upsertResult.RawResourceElement.InstanceType, upsertResult.RawResourceElement.Id, upsertResult.RawResourceElement.VersionId); FhirCosmosResourceWrapper originalWrapper = (FhirCosmosResourceWrapper)await _dataStore.GetAsync(resourceKey, CancellationToken.None); // Add new search index entry to existing wrapper. SearchParameterInfo searchParamInfo = new SearchParameterInfo("newSearchParam"); SearchIndexEntry searchIndex = new SearchIndexEntry(searchParamInfo, new NumberSearchValue(12)); List <SearchIndexEntry> searchIndices = new List <SearchIndexEntry>() { searchIndex }; var updatedWrapper = new ResourceWrapper( updatedId == null ? originalWrapper.Id : updatedId, originalWrapper.Version, originalWrapper.ResourceTypeName, originalWrapper.RawResource, originalWrapper.Request, originalWrapper.LastModified, deleted: false, searchIndices, originalWrapper.CompartmentIndices, originalWrapper.LastModifiedClaims); return(originalWrapper, updatedWrapper); }
public async Task <Bundle> SearchHistoryAsync( string resourceType, string resourceId, PartialDateTime at, PartialDateTime since, int?count, string continuationToken, CancellationToken cancellationToken) { var queryParameters = new List <Tuple <string, string> >(); if (at != null && since != null) { // _at and _since cannot be both specified. throw new InvalidSearchOperationException( string.Format( CultureInfo.InvariantCulture, Core.Resources.AtAndSinceCannotBeBothSpecified, KnownQueryParameterNames.At, KnownQueryParameterNames.Since)); } bool searchByResourceId = !string.IsNullOrEmpty(resourceId); if (searchByResourceId) { queryParameters.Add(Tuple.Create(SearchParameterNames.Id, resourceId)); } if (!string.IsNullOrEmpty(continuationToken)) { queryParameters.Add(Tuple.Create(KnownQueryParameterNames.ContinuationToken, continuationToken)); } if (at != null) { queryParameters.Add(Tuple.Create(SearchParameterNames.LastUpdated, at.ToString())); } else if (since != null) { queryParameters.Add(Tuple.Create(SearchParameterNames.LastUpdated, $"ge{since}")); } if (count.HasValue && count > 0) { queryParameters.Add(Tuple.Create(KnownQueryParameterNames.Count, count.ToString())); } SearchOptions searchOptions = !string.IsNullOrEmpty(resourceType) ? _searchOptionsFactory.Create(resourceType, queryParameters) : _searchOptionsFactory.Create(queryParameters); SearchResult searchResult = await SearchHistoryInternalAsync(searchOptions, cancellationToken); // If no results are returned from the _history search // determine if the resource actually exists or if the results were just filtered out. // The 'deleted' state has no effect because history will return deleted resources if (searchByResourceId && searchResult.Results.Any() == false) { var resource = await _fhirDataStore.GetAsync(new ResourceKey(resourceType, resourceId), cancellationToken); if (resource == null) { throw new ResourceNotFoundException(string.Format(Core.Resources.ResourceNotFoundById, resourceType, resourceId)); } } return(_bundleFactory.CreateHistoryBundle( unsupportedSearchParams: null, result: searchResult)); }