public async Task UpdatingAResourceWithNoHistory_ThenWeCannotAccessHistoricValues()
        {
            var saveResult = await Mediator.UpsertResourceAsync(Samples.GetDefaultOrganization());

            var newResourceValues = Samples.GetDefaultOrganization()
                                    .UpdateId(saveResult.RawResourceElement.Id);

            var updateResult = await Mediator.UpsertResourceAsync(newResourceValues, WeakETag.FromVersionId(saveResult.RawResourceElement.VersionId));

            await Assert.ThrowsAsync <ResourceNotFoundException>(
                () => Mediator.GetResourceAsync(new ResourceKey <Organization>(saveResult.RawResourceElement.Id, saveResult.RawResourceElement.VersionId)));
        }
        public ConditionalPatchResourceRequest(
            string resourceType,
            JsonPatchDocument patchDocument,
            IReadOnlyList <Tuple <string, string> > conditionalParameters,
            WeakETag weakETag = null)
            : base(resourceType, conditionalParameters)
        {
            EnsureArg.IsNotNull(patchDocument, nameof(patchDocument));

            PatchDocument = patchDocument;
            WeakETag      = weakETag;
        }
        public ConditionalPatchResourceRequest(
            string resourceType,
            PatchPayload payload,
            IReadOnlyList <Tuple <string, string> > conditionalParameters,
            WeakETag weakETag = null)
            : base(resourceType, conditionalParameters)
        {
            EnsureArg.IsNotNull(payload, nameof(payload));

            Payload  = payload;
            WeakETag = weakETag;
        }
Beispiel #4
0
        public Bundle CreateHistoryBundle(IEnumerable <Tuple <string, string> > unsupportedSearchParameters, SearchResult result)
        {
            // Create the bundle from the result.
            var bundle = new Bundle();

            if (result != null)
            {
                IEnumerable <Bundle.EntryComponent> entries = result.Results.Select(r =>
                {
                    Resource resource = _deserializer.Deserialize(r);
                    var hasVerb       = Enum.TryParse(r.Request?.Method, true, out Bundle.HTTPVerb httpVerb);

                    return(new Bundle.EntryComponent
                    {
                        FullUrlElement = new FhirUri(_urlResolver.ResolveResourceUrl(resource, true)),
                        Resource = resource,
                        Request = new Bundle.RequestComponent
                        {
                            Method = hasVerb ? (Bundle.HTTPVerb?)httpVerb: null,
                            Url = r.Request?.Url?.ToString(),
                        },
                        Response = new Bundle.ResponseComponent
                        {
                            LastModified = r.LastModified,
                            Etag = WeakETag.FromVersionId(r.Version).ToString(),
                        },
                    });
                });

                bundle.Entry.AddRange(entries);

                if (result.ContinuationToken != null)
                {
                    bundle.NextLink = _urlResolver.ResolveRouteUrl(
                        unsupportedSearchParameters,
                        result.ContinuationToken);
                }
            }

            // Add the self link to indicate which search parameters were used.
            bundle.SelfLink = _urlResolver.ResolveRouteUrl(unsupportedSearchParameters);

            bundle.Id    = _fhirRequestContextAccessor.FhirRequestContext.CorrelationId;
            bundle.Type  = Bundle.BundleType.History;
            bundle.Total = result?.TotalCount;
            bundle.Meta  = new Meta
            {
                LastUpdated = Clock.UtcNow,
            };

            return(bundle);
        }
        public async Task <ReindexJobWrapper> UpdateReindexJobAsync(ReindexJobRecord jobRecord, WeakETag eTag, CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNull(jobRecord, nameof(jobRecord));

            var cosmosReindexJob = new CosmosReindexJobRecordWrapper(jobRecord);

            var requestOptions = new RequestOptions()
            {
                PartitionKey = new PartitionKey(CosmosDbReindexConstants.ReindexJobPartitionKey),
            };

            // Create access condition so that record is replaced only if eTag matches.
            if (eTag != null)
            {
                requestOptions.AccessCondition = new AccessCondition()
                {
                    Type      = AccessConditionType.IfMatch,
                    Condition = eTag.VersionId,
                };
            }

            try
            {
                ResourceResponse <Document> replaceResult = await _retryExceptionPolicyFactory.CreateRetryPolicy().ExecuteAsync(
                    () => _documentClientScope.Value.ReplaceDocumentAsync(
                        UriFactory.CreateDocumentUri(DatabaseId, CollectionId, jobRecord.Id),
                        cosmosReindexJob,
                        requestOptions,
                        cancellationToken));

                var latestETag = replaceResult.Resource.ETag;
                return(new ReindexJobWrapper(jobRecord, WeakETag.FromVersionId(latestETag)));
            }
            catch (DocumentClientException dce)
            {
                if (dce.StatusCode == HttpStatusCode.TooManyRequests)
                {
                    throw new RequestRateExceededException(dce.RetryAfter);
                }
                else if (dce.StatusCode == HttpStatusCode.PreconditionFailed)
                {
                    throw new JobConflictException();
                }
                else if (dce.StatusCode == HttpStatusCode.NotFound)
                {
                    throw new JobNotFoundException(string.Format(Core.Resources.JobNotFound, jobRecord.Id));
                }

                _logger.LogError(dce, "Failed to update a reindex job.");
                throw;
            }
        }
Beispiel #6
0
        public async Task UpdatingAResourceWithNoHistory_ThenWeCannotAccessHistoricValues()
        {
            var saveResult = await FhirRepository.UpsertAsync(Samples.GetDefaultOrganization());

            var newResourceValues = Samples.GetDefaultOrganization();

            newResourceValues.Id = saveResult.Resource.Id;

            var updateResult = await FhirRepository.UpsertAsync(newResourceValues, WeakETag.FromVersionId(saveResult.Resource.VersionId));

            await Assert.ThrowsAsync <ResourceNotFoundException>(
                () => FhirRepository.GetAsync(new ResourceKey <Organization>(saveResult.Resource.Id, saveResult.Resource.VersionId)));
        }
Beispiel #7
0
 /// <summary>
 /// For each result in a batch of resources this will extract new search params
 /// Then compare those to the old values to determine if a change is needed
 /// Needed updates will be processed in a batch
 /// </summary>
 /// <param name="results">The resource batch to process</param>
 /// <param name="searchParamHash">the current hash value of the search parameters</param>
 /// <param name="cancellationToken">Cancellation token</param>
 /// <returns>A Task</returns>
 public async Task ProcessSearchResultsAsync(SearchResult results, string searchParamHash, CancellationToken cancellationToken)
 {
     // TODO: placeholder, will be updated to extract new indices, compare with old values and update indices
     // It should update only the indices and search parameter hash as a bulk operation, and not affect lastUpdated timestamp
     using (IScoped <IFhirDataStore> store = _fhirDataStoreFactory())
     {
         foreach (var result in results.Results)
         {
             result.Resource.SearchParameterHash = searchParamHash;
             await store.Value.UpsertAsync(result.Resource, WeakETag.FromVersionId(result.Resource.Version), false, true, cancellationToken);
         }
     }
 }
        public async Task GivenAGetRequest_WhenIdNotFound_ThenJobNotFoundExceptionThrown()
        {
            var request = new GetReindexRequest("id");

            var jobRecord  = new ReindexJobRecord("hash", 1, null);
            var jobWrapper = new ReindexJobWrapper(jobRecord, WeakETag.FromVersionId("id"));

            _fhirOperationDataStore.GetReindexJobByIdAsync("id", Arg.Any <CancellationToken>()).Throws(new JobNotFoundException("not found"));

            var handler = new GetReindexRequestHandler(_fhirOperationDataStore, DisabledFhirAuthorizationService.Instance);

            await Assert.ThrowsAsync <JobNotFoundException>(() => handler.Handle(request, CancellationToken.None));
        }
Beispiel #9
0
        public async Task <DeleteResourceResponse> Handle(DeleteResourceRequest message, CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNull(message, nameof(message));

            var deletedResourceKey = await _repository.DeleteAsync(message.ResourceKey, message.HardDelete, cancellationToken);

            if (string.IsNullOrWhiteSpace(deletedResourceKey.VersionId))
            {
                return(new DeleteResourceResponse());
            }

            return(new DeleteResourceResponse(WeakETag.FromVersionId(deletedResourceKey.VersionId)));
        }
        public async Task GivenAGetRequest_WhenTooManyRequestsThrown_ThenTooManyRequestsThrown()
        {
            var request = new GetReindexRequest("id");

            var jobRecord  = new ReindexJobRecord("hash", 1, null);
            var jobWrapper = new ReindexJobWrapper(jobRecord, WeakETag.FromVersionId("id"));

            _fhirOperationDataStore.GetReindexJobByIdAsync("id", CancellationToken.None).Throws(new RequestRateExceededException(TimeSpan.FromMilliseconds(100)));

            var handler = new GetReindexRequestHandler(_fhirOperationDataStore, DisabledFhirAuthorizationService.Instance);

            await Assert.ThrowsAsync <RequestRateExceededException>(() => handler.Handle(request, CancellationToken.None));
        }
Beispiel #11
0
        public async Task GivenADeletedIdAndVersionId_WhenGettingAResource_TheServerShouldReturnAGoneStatus()
        {
            Observation createdResource = await _client.CreateAsync(Samples.GetDefaultObservation().ToPoco <Observation>());

            using FhirResponse deleteResponse = await _client.DeleteAsync(createdResource);

            var deletedVersion = WeakETag.FromWeakETag(deleteResponse.Headers.ETag.ToString()).VersionId;

            using FhirException ex = await Assert.ThrowsAsync <FhirException>(
                      () => _client.VReadAsync <Observation>(ResourceType.Observation, createdResource.Id, deletedVersion));

            Assert.Equal(System.Net.HttpStatusCode.Gone, ex.StatusCode);
        }
Beispiel #12
0
        public async Task <UpsertResourceResponse> Handle(ConditionalUpsertResourceRequest message, CancellationToken cancellationToken = default)
        {
            EnsureArg.IsNotNull(message, nameof(message));

            if (await AuthorizationService.CheckAccess(DataActions.Read | DataActions.Write) != (DataActions.Read | DataActions.Write))
            {
                throw new UnauthorizedFhirActionException();
            }

            SearchResultEntry[] matchedResults = await Search(message.Resource.InstanceType, message.ConditionalParameters, cancellationToken);

            int count = matchedResults.Length;

            if (count == 0)
            {
                if (string.IsNullOrEmpty(message.Resource.Id))
                {
                    // No matches, no id provided: The server creates the resource
                    // TODO: There is a potential contention issue here in that this could create another new resource with a different id
                    return(await _mediator.Send <UpsertResourceResponse>(new CreateResourceRequest(message.Resource), cancellationToken));
                }
                else
                {
                    // No matches, id provided: The server treats the interaction as an Update as Create interaction (or rejects it, if it does not support Update as Create)
                    // TODO: There is a potential contention issue here that this could replace an existing resource
                    return(await _mediator.Send <UpsertResourceResponse>(new UpsertResourceRequest(message.Resource), cancellationToken));
                }
            }
            else if (count == 1)
            {
                ResourceWrapper resourceWrapper = matchedResults.First().Resource;
                Resource        resource        = message.Resource.ToPoco();
                WeakETag        version         = WeakETag.FromVersionId(resourceWrapper.Version);

                // One Match, no resource id provided OR (resource id provided and it matches the found resource): The server performs the update against the matching resource
                if (string.IsNullOrEmpty(resource.Id) || string.Equals(resource.Id, resourceWrapper.ResourceId, StringComparison.Ordinal))
                {
                    resource.Id = resourceWrapper.ResourceId;
                    return(await _mediator.Send <UpsertResourceResponse>(new UpsertResourceRequest(resource.ToResourceElement(), version), cancellationToken));
                }
                else
                {
                    throw new BadRequestException(string.Format(Core.Resources.ConditionalUpdateMismatchedIds, resourceWrapper.ResourceId, resource.Id));
                }
            }
            else
            {
                // Multiple matches: The server returns a 412 Precondition Failed error indicating the client's criteria were not selective enough
                throw new PreconditionFailedException(Core.Resources.ConditionalOperationNotSelectiveEnough);
            }
        }
Beispiel #13
0
        public async void GivenAFhirMediator_WhenGettingAnExistingExportJobWithNotCompletedStatus_ThenHttpResponseCodeShouldBeAccepted()
        {
            var jobRecord = new ExportJobRecord(new Uri(CreateRequestUrl));

            jobRecord.Status = OperationStatus.Running;
            var jobOutcome = new ExportJobOutcome(jobRecord, WeakETag.FromVersionId("eTag"));

            _fhirDataStore.GetExportJobAsync(jobRecord.Id, Arg.Any <CancellationToken>()).Returns(jobOutcome);

            var result = await _mediator.GetExportStatusAsync(new Uri(CreateRequestUrl), jobRecord.Id);

            Assert.Equal(HttpStatusCode.Accepted, result.StatusCode);
            Assert.Null(result.JobResult);
        }
        private static byte[] GetRowVersionAsBytes(WeakETag eTag)
        {
            // The SQL rowversion data type is 8 bytes in length.
            var versionAsBytes = new byte[8];

            BitConverter.TryWriteBytes(versionAsBytes, int.Parse(eTag.VersionId));

            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(versionAsBytes);
            }

            return(versionAsBytes);
        }
        public async Task GivenAnOldVersionOfAJob_WhenUpdatingTheJob_ThenJobConflictExceptionShouldBeThrown()
        {
            ExportJobOutcome jobOutcome = await CreateRunningJob();

            ExportJobRecord job = jobOutcome.JobRecord;

            // Update the job for a first time. This should not fail.
            job.Status = OperationStatus.Completed;
            WeakETag jobVersion = jobOutcome.ETag;
            await _operationDataStore.UpdateExportJobAsync(job, jobVersion, CancellationToken.None);

            // Attempt to update the job a second time with the old version.
            await Assert.ThrowsAsync <JobConflictException>(() => _operationDataStore.UpdateExportJobAsync(job, jobVersion, CancellationToken.None));
        }
        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 GivenAGetRequest_WhenGettingAnExistingJob_ThenHttpResponseCodeShouldBeOk()
        {
            var request = new GetReindexRequest("id");

            var jobRecord  = new ReindexJobRecord("hash", 1, null);
            var jobWrapper = new ReindexJobWrapper(jobRecord, WeakETag.FromVersionId("id"));

            _fhirOperationDataStore.GetReindexJobByIdAsync("id", Arg.Any <CancellationToken>()).Returns(jobWrapper);

            var handler = new GetReindexRequestHandler(_fhirOperationDataStore, DisabledFhirAuthorizationService.Instance);

            var result = await handler.Handle(request, CancellationToken.None);

            Assert.Equal(HttpStatusCode.OK, result.StatusCode);
        }
        public async Task GivenAGetRequest_WhenTooManyRequestsThrown_ThenTooManyRequestsThrown()
        {
            var request = new GetReindexRequest("id");

            var jobRecord  = new ReindexJobRecord(_resourceTypeSearchParameterHashMap, 1);
            var jobWrapper = new ReindexJobWrapper(jobRecord, WeakETag.FromVersionId("id"));

            _fhirOperationDataStore.GetReindexJobByIdAsync("id", CancellationToken.None).Throws(new Exception(null, new RequestRateExceededException(TimeSpan.FromMilliseconds(100))));

            var handler = new GetReindexRequestHandler(_fhirOperationDataStore, DisabledFhirAuthorizationService.Instance);

            Exception thrownException = await Assert.ThrowsAsync <Exception>(() => handler.Handle(request, CancellationToken.None));

            Assert.IsType <RequestRateExceededException>(thrownException.InnerException);
        }
Beispiel #19
0
        private async System.Threading.Tasks.Task ProcessPostReindexSingleResourceRequest(ResourceWrapper originalResource, IReadOnlyCollection <SearchIndexEntry> searchIndices)
        {
            ResourceWrapper updatedResource = new ResourceWrapper(
                originalResource.ResourceId,
                originalResource.Version,
                originalResource.ResourceTypeName,
                originalResource.RawResource,
                originalResource.Request,
                originalResource.LastModified,
                deleted: false,
                searchIndices,
                originalResource.CompartmentIndices,
                originalResource.LastModifiedClaims);

            await _fhirDataStore.UpdateSearchIndexForResourceAsync(updatedResource, WeakETag.FromVersionId(originalResource.Version), CancellationToken.None);
        }
Beispiel #20
0
        public async Task GivenASavedResource_WhenUpserting_ThenTheExistingResourceIsUpdated()
        {
            var saveResult = await Mediator.UpsertResourceAsync(Samples.GetJsonSample("Weight"));

            var newResourceValues = Samples.GetJsonSample("WeightInGrams");

            newResourceValues.Id = saveResult.Resource.Id;

            var updateResult = await Mediator.UpsertResourceAsync(newResourceValues, WeakETag.FromVersionId(saveResult.Resource.VersionId));

            Assert.NotNull(updateResult);
            Assert.Equal(SaveOutcomeType.Updated, updateResult.Outcome);

            Assert.NotNull(updateResult.Resource);
            Assert.Equal(saveResult.Resource.Id, updateResult.Resource.Id);
        }
Beispiel #21
0
        public async Task GivenAnUpdatedResourceWithWrongWeakETag_WhenUpdatingSearchParameterIndexAsync_ThenExceptionIsThrown()
        {
            ResourceElement patientResource = CreatePatientResourceElement("Patient", Guid.NewGuid().ToString());
            SaveOutcome     upsertResult    = await Mediator.UpsertResourceAsync(patientResource);

            SearchParameter searchParam1     = null;
            const string    searchParamName1 = "newSearchParam1";

            SearchParameter searchParam2     = null;
            const string    searchParamName2 = "newSearchParam2";

            try
            {
                searchParam1 = await CreatePatientSearchParam(searchParamName1, SearchParamType.String, "Patient.name");

                ISearchValue searchValue1 = new StringSearchValue(searchParamName1);

                (ResourceWrapper original, ResourceWrapper updatedWithSearchParam1) = await CreateUpdatedWrapperFromExistingPatient(upsertResult, searchParam1, searchValue1);

                await _dataStore.UpsertAsync(updatedWithSearchParam1, WeakETag.FromVersionId(original.Version), allowCreate : false, keepHistory : false, CancellationToken.None);

                // Let's update the resource again with new information
                searchParam2 = await CreatePatientSearchParam(searchParamName2, SearchParamType.Token, "Patient.gender");

                ISearchValue searchValue2 = new TokenSearchValue("system", "code", "text");

                // Create the updated wrapper from the original resource that has the outdated version
                (_, ResourceWrapper updatedWithSearchParam2) = await CreateUpdatedWrapperFromExistingPatient(upsertResult, searchParam2, searchValue2, original);

                // Attempt to reindex the resource
                await Assert.ThrowsAsync <PreconditionFailedException>(() => _dataStore.UpdateSearchParameterIndicesAsync(updatedWithSearchParam2, WeakETag.FromVersionId(original.Version), CancellationToken.None));
            }
            finally
            {
                if (searchParam1 != null)
                {
                    _searchParameterDefinitionManager.DeleteSearchParameter(searchParam1.ToTypedElement());
                    await _fixture.TestHelper.DeleteSearchParameterStatusAsync(searchParam1.Url, CancellationToken.None);
                }

                if (searchParam2 != null)
                {
                    _searchParameterDefinitionManager.DeleteSearchParameter(searchParam2.ToTypedElement());
                    await _fixture.TestHelper.DeleteSearchParameterStatusAsync(searchParam2.Url, CancellationToken.None);
                }
            }
        }
        public async Task GivenACancelRequest_WhenUserUnauthorized_ThenUnauthorizedFhirExceptionThrown()
        {
            var request = new CancelReindexRequest("id");

            var jobRecord  = new ReindexJobRecord("hash", 1, null);
            var jobWrapper = new ReindexJobWrapper(jobRecord, WeakETag.FromVersionId("id"));

            _fhirOperationDataStore.GetReindexJobByIdAsync("id", Arg.Any <CancellationToken>()).Returns(jobWrapper);

            var authorizationService = Substitute.For <IFhirAuthorizationService>();

            authorizationService.CheckAccess(DataActions.Reindex).Returns(DataActions.None);

            var handler = new CancelReindexRequestHandler(_fhirOperationDataStore, authorizationService);

            await Assert.ThrowsAsync <UnauthorizedFhirActionException>(() => handler.Handle(request, CancellationToken.None));
        }
        private async Task UpdateJobRecordAsync(CancellationToken cancellationToken)
        {
            foreach (OperationOutcomeIssue issue in _contextAccessor.FhirRequestContext.BundleIssues)
            {
                _exportJobRecord.Issues.Add(issue);
            }

            using (IScoped <IFhirOperationDataStore> fhirOperationDataStore = _fhirOperationDataStoreFactory())
            {
                ExportJobOutcome updatedExportJobOutcome = await fhirOperationDataStore.Value.UpdateExportJobAsync(_exportJobRecord, _weakETag, cancellationToken);

                _exportJobRecord = updatedExportJobOutcome.JobRecord;
                _weakETag        = updatedExportJobOutcome.ETag;

                _contextAccessor.FhirRequestContext.BundleIssues.Clear();
            }
        }
        public async Task GivenACancelRequest_WhenJobCompleted_ThenRequestNotValidExceptionThrown()
        {
            var request = new CancelReindexRequest("id");

            var jobRecord = new ReindexJobRecord("hash", 1, null)
            {
                Status = OperationStatus.Completed,
            };

            var jobWrapper = new ReindexJobWrapper(jobRecord, WeakETag.FromVersionId("id"));

            _fhirOperationDataStore.GetReindexJobByIdAsync("id", Arg.Any <CancellationToken>()).Returns(jobWrapper);

            var handler = new CancelReindexRequestHandler(_fhirOperationDataStore, DisabledFhirAuthorizationService.Instance);

            await Assert.ThrowsAsync <RequestNotValidException>(() => handler.Handle(request, CancellationToken.None));
        }
        public override async Task <UpsertResourceResponse> HandleSingleMatch(ConditionalUpsertResourceRequest request, SearchResultEntry match, CancellationToken cancellationToken)
        {
            ResourceWrapper resourceWrapper = match.Resource;
            Resource        resource        = request.Resource.ToPoco();
            var             version         = WeakETag.FromVersionId(resourceWrapper.Version);

            // One Match, no resource id provided OR (resource id provided and it matches the found resource): The server performs the update against the matching resource
            if (string.IsNullOrEmpty(resource.Id) || string.Equals(resource.Id, resourceWrapper.ResourceId, StringComparison.Ordinal))
            {
                resource.Id = resourceWrapper.ResourceId;
                return(await _mediator.Send <UpsertResourceResponse>(new UpsertResourceRequest(resource.ToResourceElement(), version), cancellationToken));
            }
            else
            {
                throw new BadRequestException(string.Format(Core.Resources.ConditionalUpdateMismatchedIds, resourceWrapper.ResourceId, resource.Id));
            }
        }
Beispiel #26
0
        public async Task GivenAGetRequest_WhenUserUnauthorized_ThenUnauthorizedFhirExceptionThrown()
        {
            var request = new GetReindexRequest("id");

            var jobRecord  = new ReindexJobRecord(_resourceTypeSearchParameterHashMap, new List <string>(), 1);
            var jobWrapper = new ReindexJobWrapper(jobRecord, WeakETag.FromVersionId("id"));

            _fhirOperationDataStore.GetReindexJobByIdAsync("id", Arg.Any <CancellationToken>()).Returns(jobWrapper);

            var authorizationService = Substitute.For <IAuthorizationService <DataActions> >();

            authorizationService.CheckAccess(DataActions.Reindex, Arg.Any <CancellationToken>()).Returns(DataActions.None);

            var handler = new GetReindexRequestHandler(_fhirOperationDataStore, authorizationService);

            await Assert.ThrowsAsync <UnauthorizedFhirActionException>(() => handler.Handle(request, CancellationToken.None));
        }
        public async Task <ReindexJobWrapper> UpdateReindexJobAsync(ReindexJobRecord jobRecord, WeakETag eTag, CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNull(jobRecord, nameof(jobRecord));

            var cosmosReindexJob = new CosmosReindexJobRecordWrapper(jobRecord);
            var requestOptions   = new ItemRequestOptions();

            // Create access condition so that record is replaced only if eTag matches.
            if (eTag != null)
            {
                requestOptions.IfMatchEtag = eTag.VersionId;
            }

            try
            {
                var replaceResult = await _retryExceptionPolicyFactory.CreateRetryPolicy().ExecuteAsync(
                    () => _containerScope.Value.ReplaceItemAsync(
                        cosmosReindexJob,
                        jobRecord.Id,
                        new PartitionKey(CosmosDbReindexConstants.ReindexJobPartitionKey),
                        requestOptions,
                        cancellationToken));

                var latestETag = replaceResult.Resource.ETag;
                return(new ReindexJobWrapper(jobRecord, WeakETag.FromVersionId(latestETag)));
            }
            catch (CosmosException dce)
            {
                if (dce.IsRequestRateExceeded())
                {
                    throw;
                }
                else if (dce.StatusCode == HttpStatusCode.PreconditionFailed)
                {
                    throw new JobConflictException();
                }
                else if (dce.StatusCode == HttpStatusCode.NotFound)
                {
                    throw new JobNotFoundException(string.Format(Core.Resources.JobNotFound, jobRecord.Id));
                }

                _logger.LogError(dce, "Failed to update a reindex job.");
                throw;
            }
        }
        public async Task GivenAResourceTypeWithNoVersionVersioningPolicy_WhenSearchingHistory_ThenOnlyLatestVersionIsReturned()
        {
            // The FHIR storage fixture configures organization resources to have the "no-version" versioning policy
            RawResourceElement organizationResource = await Mediator.CreateResourceAsync(Samples.GetDefaultOrganization());

            ResourceElement newResourceValues = Samples.GetDefaultOrganization().UpdateId(organizationResource.Id);

            SaveOutcome updateResult = await Mediator.UpsertResourceAsync(newResourceValues, WeakETag.FromVersionId(organizationResource.VersionId));

            ResourceElement historyResults = await Mediator.SearchResourceHistoryAsync(KnownResourceTypes.Organization, updateResult.RawResourceElement.Id);

            // The history bundle only has one entry because resource history is not kept
            Bundle bundle = historyResults.ToPoco <Bundle>();

            Assert.Single(bundle.Entry);

            Assert.Equal(WeakETag.FromVersionId(updateResult.RawResourceElement.VersionId).ToString(), bundle.Entry[0].Response.Etag);
        }
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        EnsureArg.IsNotNull(bindingContext, nameof(bindingContext));

        var suppliedWeakETag = bindingContext.HttpContext.Request.Headers[HeaderNames.IfMatch];

        WeakETag model = null;

        if (!string.IsNullOrWhiteSpace(suppliedWeakETag))
        {
            model = WeakETag.FromWeakETag(suppliedWeakETag);
        }

        bindingContext.Model  = model;
        bindingContext.Result = ModelBindingResult.Success(model);

        return(Task.CompletedTask);
    }
Beispiel #30
0
        public ResourceElement CreateHistoryBundle(SearchResult result)
        {
            return(CreateBundle(result, Bundle.BundleType.History, r =>
            {
                var resource = new RawBundleEntryComponent(r.Resource);
                var hasVerb = Enum.TryParse(r.Resource.Request?.Method, true, out Bundle.HTTPVerb httpVerb);

                resource.FullUrlElement = new FhirUri(_urlResolver.ResolveResourceWrapperUrl(r.Resource, true));
                resource.Request = new Bundle.RequestComponent
                {
                    Method = hasVerb ? httpVerb : null,
                    Url = hasVerb ? $"{r.Resource.ResourceTypeName}/{(httpVerb == Bundle.HTTPVerb.POST ? null : r.Resource.ResourceId)}" : null,
                };

                string statusString;
                switch (httpVerb)
                {
                case Bundle.HTTPVerb.POST:
                    statusString = ((int)HttpStatusCode.Created).ToString() + " " + HttpStatusCode.Created;
                    break;

                case Bundle.HTTPVerb.PUT:
                case Bundle.HTTPVerb.GET:
                    statusString = ((int)HttpStatusCode.OK).ToString() + " " + HttpStatusCode.OK;
                    break;

                case Bundle.HTTPVerb.DELETE:
                    statusString = ((int)HttpStatusCode.NoContent).ToString() + " " + HttpStatusCode.NoContent;
                    break;

                default:
                    throw new NotImplementedException();
                }

                resource.Response = new Bundle.ResponseComponent
                {
                    Status = statusString,
                    LastModified = r.Resource.LastModified,
                    Etag = WeakETag.FromVersionId(r.Resource.Version).ToString(),
                };

                return resource;
            }));
        }