public async Task <ReindexJobWrapper> GetReindexJobByIdAsync(string jobId, CancellationToken cancellationToken) { EnsureArg.IsNotNullOrWhiteSpace(jobId, nameof(jobId)); try { DocumentResponse <CosmosReindexJobRecordWrapper> cosmosReindexJobRecord = await _documentClientScope.Value.ReadDocumentAsync <CosmosReindexJobRecordWrapper>( UriFactory.CreateDocumentUri(DatabaseId, CollectionId, jobId), new RequestOptions { PartitionKey = new PartitionKey(CosmosDbReindexConstants.ReindexJobPartitionKey) }, cancellationToken); var outcome = new ReindexJobWrapper(cosmosReindexJobRecord.Document.JobRecord, WeakETag.FromVersionId(cosmosReindexJobRecord.Document.ETag)); return(outcome); } catch (DocumentClientException dce) { if (dce.StatusCode == HttpStatusCode.TooManyRequests) { throw new RequestRateExceededException(dce.RetryAfter); } else if (dce.StatusCode == HttpStatusCode.NotFound) { throw new JobNotFoundException(string.Format(Core.Resources.JobNotFound, jobId)); } _logger.LogError(dce, $"Failed to get reindex job by id: {jobId}."); throw; } }
public void GivenAVersion_WhenAddingETagDecoration_AWeakEtagShouldBeReturned() { var weakETag = WeakETag.FromVersionId("version1"); Assert.Equal("W/\"version1\"", weakETag.ToString()); }
public override void OnActionExecuted(ActionExecutedContext context) { EnsureArg.IsNotNull(context, nameof(context)); if (context?.Exception is FhirException fhirException) { FhirResult fhirResult = FhirResult.Create( new OperationOutcome { Id = _fhirRequestContextAccessor.FhirRequestContext.CorrelationId, Issue = fhirException.Issues.ToList(), }, HttpStatusCode.BadRequest); switch (fhirException) { case ResourceGoneException resourceGoneException: fhirResult.StatusCode = HttpStatusCode.Gone; if (!string.IsNullOrEmpty(resourceGoneException.DeletedResource?.VersionId)) { fhirResult.SetETagHeader(WeakETag.FromVersionId(resourceGoneException.DeletedResource.VersionId)); } break; case ResourceNotFoundException _: fhirResult.StatusCode = HttpStatusCode.NotFound; break; case MethodNotAllowedException _: fhirResult.StatusCode = HttpStatusCode.MethodNotAllowed; break; case ServiceUnavailableException _: case OpenIdConfigurationException _: fhirResult.StatusCode = HttpStatusCode.ServiceUnavailable; break; case ResourceNotValidException _: case BadRequestException _: fhirResult.StatusCode = HttpStatusCode.BadRequest; break; case ResourceConflictException _: fhirResult.StatusCode = HttpStatusCode.Conflict; break; case UnsupportedMediaTypeException _: fhirResult.StatusCode = HttpStatusCode.UnsupportedMediaType; break; case PreconditionFailedException _: fhirResult.StatusCode = HttpStatusCode.PreconditionFailed; break; case InvalidSearchOperationException _: case SearchOperationNotSupportedException _: fhirResult.StatusCode = HttpStatusCode.Forbidden; break; case UnsupportedConfigurationException _: fhirResult.StatusCode = HttpStatusCode.InternalServerError; break; case RequestRateExceededException ex: fhirResult.StatusCode = HttpStatusCode.TooManyRequests; if (ex.RetryAfter != null) { fhirResult.Headers.Add( RetryAfterHeaderName, ex.RetryAfter.Value.TotalMilliseconds.ToString(CultureInfo.InvariantCulture)); } break; } context.Result = fhirResult; context.ExceptionHandled = true; } }
public async Task GivenANonexistentResourceAndCosmosDb_WhenUpsertingWithCreateEnabledAndInvalidETagHeader_ThenResourceNotFoundIsThrown() { SetAllowCreate(true); await Assert.ThrowsAsync <ResourceNotFoundException>(() => Mediator.UpsertResourceAsync(Samples.GetJsonSample("Weight"), WeakETag.FromVersionId("invalidVersion"))); }
public void GivenANonWeakETag_WhenRemovingETagDecoration_ThenOriginalShouldRemain() { var weakETag = WeakETag.FromVersionId("\"version1\""); Assert.Equal("\"version1\"", weakETag.VersionId); }
public async Task GivenAResourceTypeWithVersionedUpdateVersioningPolicy_WhenSearchingHistory_ThenAllVersionsAreReturned() { // The FHIR storage fixture configures medication resources to have the "versioned-update" versioning policy RawResourceElement medicationResource = await Mediator.CreateResourceAsync(Samples.GetDefaultMedication()); ResourceElement newResourceValues = Samples.GetDefaultMedication().UpdateId(medicationResource.Id); SaveOutcome updateResult = await Mediator.UpsertResourceAsync(newResourceValues, WeakETag.FromVersionId(medicationResource.VersionId)); ResourceElement historyResults = await Mediator.SearchResourceHistoryAsync(KnownResourceTypes.Medication, updateResult.RawResourceElement.Id); // The history bundle has both versions because history is kept Bundle bundle = historyResults.ToPoco <Bundle>(); Assert.Equal(2, bundle.Entry.Count); Assert.Equal(WeakETag.FromVersionId(updateResult.RawResourceElement.VersionId).ToString(), bundle.Entry.Max(entry => entry.Response.Etag)); Assert.Equal(WeakETag.FromVersionId(medicationResource.VersionId).ToString(), bundle.Entry.Min(entry => entry.Response.Etag)); }
public async Task GivenASavedResource_WhenUpserting_ThenTheExistingResourceIsUpdated() { var saveResult = await Mediator.UpsertResourceAsync(Samples.GetJsonSample("Weight")); var newResourceValues = Samples.GetJsonSample("WeightInGrams").ToPoco(); newResourceValues.Id = saveResult.Resource.Id; var updateResult = await Mediator.UpsertResourceAsync(newResourceValues.ToResourceElement(), 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); }
public async Task GivenAnUpdatedResourceWithWrongResourceId_WhenUpdatingSearchParameterIndexAsync_ThenExceptionIsThrown() { 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); // Update the resource wrapper, adding the new search parameter and a different ID (ResourceWrapper original, ResourceWrapper updated) = await CreateUpdatedWrapperFromExistingPatient(upsertResult, searchParam, searchValue, null, Guid.NewGuid().ToString()); await Assert.ThrowsAsync <ResourceNotFoundException>(() => _dataStore.UpdateSearchParameterIndicesAsync(updated, WeakETag.FromVersionId(original.Version), CancellationToken.None)); } finally { if (searchParam != null) { _searchParameterDefinitionManager.DeleteSearchParameter(searchParam.ToTypedElement()); await _fixture.TestHelper.DeleteSearchParameterStatusAsync(searchParam.Url, CancellationToken.None); } } }
public async Task GivenAResourceTypeWithVersionedUpdateVersioningPolicy_WhenUpsertingWithNonMatchingVersion_ThenAPreconditionFailedExceptionIsThrown() { // The FHIR storage fixture configures medication resources to have the "versioned-update" versioning policy RawResourceElement medicationResource = await Mediator.CreateResourceAsync(Samples.GetDefaultMedication()); ResourceElement newResourceValues = Samples.GetDefaultMedication().UpdateId(medicationResource.Id); // Pass in a version that does not match the most recent version of the resource being updated // This simulates a request where a non-matching version is specified in the if-match header const string incorrectVersion = "2"; var exception = await Assert.ThrowsAsync <PreconditionFailedException>(async() => await Mediator.UpsertResourceAsync(newResourceValues, WeakETag.FromVersionId(incorrectVersion))); Assert.Equal(string.Format(Core.Resources.ResourceVersionConflict, incorrectVersion), exception.Message); }
public override void OnActionExecuted(ActionExecutedContext context) { EnsureArg.IsNotNull(context, nameof(context)); if (context?.Exception == null) { return; } if (context.Exception is FhirException fhirException) { var operationOutcomeResult = new OperationOutcomeResult( new OperationOutcome { Id = _fhirRequestContextAccessor.FhirRequestContext.CorrelationId, Issue = fhirException.Issues.Select(x => x.ToPoco()).ToList(), }, HttpStatusCode.BadRequest); switch (fhirException) { case ResourceGoneException resourceGoneException: operationOutcomeResult.StatusCode = HttpStatusCode.Gone; if (!string.IsNullOrEmpty(resourceGoneException.DeletedResource?.VersionId)) { operationOutcomeResult.Headers.Add(HeaderNames.ETag, WeakETag.FromVersionId(resourceGoneException.DeletedResource.VersionId).ToString()); } break; case ResourceNotFoundException _: case JobNotFoundException _: operationOutcomeResult.StatusCode = HttpStatusCode.NotFound; break; case MethodNotAllowedException _: operationOutcomeResult.StatusCode = HttpStatusCode.MethodNotAllowed; break; case ServiceUnavailableException _: case OpenIdConfigurationException _: operationOutcomeResult.StatusCode = HttpStatusCode.ServiceUnavailable; break; case ResourceNotValidException _: if (context.ActionDescriptor is ControllerActionDescriptor controllerDescriptor) { if (controllerDescriptor.ControllerName.Equals(ValidateController, StringComparison.OrdinalIgnoreCase)) { operationOutcomeResult.StatusCode = HttpStatusCode.OK; break; } } operationOutcomeResult.StatusCode = HttpStatusCode.BadRequest; break; case BadRequestException _: case RequestNotValidException _: case BundleEntryLimitExceededException _: operationOutcomeResult.StatusCode = HttpStatusCode.BadRequest; break; case ResourceConflictException _: operationOutcomeResult.StatusCode = HttpStatusCode.Conflict; break; case UnsupportedMediaTypeException _: operationOutcomeResult.StatusCode = HttpStatusCode.UnsupportedMediaType; break; case PreconditionFailedException _: operationOutcomeResult.StatusCode = HttpStatusCode.PreconditionFailed; break; case InvalidSearchOperationException _: case SearchOperationNotSupportedException _: operationOutcomeResult.StatusCode = HttpStatusCode.Forbidden; break; case UnsupportedConfigurationException _: case AuditException _: operationOutcomeResult.StatusCode = HttpStatusCode.InternalServerError; break; case AuditHeaderException _: operationOutcomeResult.StatusCode = HttpStatusCode.RequestHeaderFieldsTooLarge; break; case OperationFailedException ofe: operationOutcomeResult.StatusCode = ofe.ResponseStatusCode; break; case OperationNotImplementedException _: operationOutcomeResult.StatusCode = HttpStatusCode.NotImplemented; break; case NotAcceptableException _: operationOutcomeResult.StatusCode = HttpStatusCode.NotAcceptable; break; case TransactionFailedException tfe: operationOutcomeResult.StatusCode = tfe.ResponseStatusCode; break; case RequestEntityTooLargeException _: operationOutcomeResult.StatusCode = HttpStatusCode.RequestEntityTooLarge; break; } context.Result = operationOutcomeResult; context.ExceptionHandled = true; } else if (context.Exception is MicrosoftHealthException microsoftHealthException) { OperationOutcomeResult healthExceptionResult; switch (microsoftHealthException) { case RequestRateExceededException ex: healthExceptionResult = new OperationOutcomeResult( new OperationOutcome { Id = _fhirRequestContextAccessor.FhirRequestContext.CorrelationId, Issue = new List <OperationOutcome.IssueComponent> { new OperationOutcome.IssueComponent { Severity = OperationOutcome.IssueSeverity.Error, Code = OperationOutcome.IssueType.Throttled, Diagnostics = ex.Message, }, }, }, HttpStatusCode.BadRequest); healthExceptionResult.StatusCode = HttpStatusCode.TooManyRequests; if (ex.RetryAfter != null) { healthExceptionResult.Headers.Add( RetryAfterHeaderName, ex.RetryAfter.Value.TotalMilliseconds.ToString(CultureInfo.InvariantCulture)); } break; default: healthExceptionResult = new OperationOutcomeResult( new OperationOutcome { Id = _fhirRequestContextAccessor.FhirRequestContext.CorrelationId, }, HttpStatusCode.InternalServerError); healthExceptionResult.StatusCode = HttpStatusCode.InternalServerError; break; } context.Result = healthExceptionResult; context.ExceptionHandled = true; } }
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 <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 void WhenAddingStringEtag_ThenStringETagIsReturned() { var fhirResult = FhirResult.Create(_mockResource).SetETagHeader(WeakETag.FromVersionId("etag")); Assert.Equal("W/\"etag\"", fhirResult.Headers[HeaderNames.ETag]); }
private ExportJobOutcome CreateExportJobOutcome() { var exportRequest = new CreateExportRequest(new Uri($"http://localhost/ExportJob/")); return(new ExportJobOutcome(new ExportJobRecord(exportRequest.RequestUri, "Patient", "hash"), WeakETag.FromVersionId("0"))); }
public async Task GivenAResourceTypeWithVersionedUpdateVersioningPolicy_WhenPutCreatingWithAVersion_ThenAResourceNotFoundExceptionIsThrown() { // The FHIR storage fixture configures medication resources to have the "versioned-update" versioning policy var randomId = Guid.NewGuid().ToString(); // Any version id on a PUT create is invalid, as we can't specify the version of a resource that does not exist const string invalidVersionId = "1"; // Upserting a resource that does not already exist in the database simulates a PUT create // Pass in an eTag to mock a request where an invalid if-match header is provided var exception = await Assert.ThrowsAsync <ResourceNotFoundException>(async() => await Mediator.UpsertResourceAsync(Samples.GetDefaultMedication().UpdateId(randomId), WeakETag.FromVersionId(invalidVersionId))); Assert.Equal(string.Format(Core.Resources.ResourceNotFoundByIdAndVersion, KnownResourceTypes.Medication, randomId, invalidVersionId), exception.Message); }
public async Task <ExportJobOutcome> GetExportJobByIdAsync(string id, CancellationToken cancellationToken) { EnsureArg.IsNotNullOrWhiteSpace(id, nameof(id)); try { ItemResponse <CosmosExportJobRecordWrapper> cosmosExportJobRecord = await _containerScope.Value.ReadItemAsync <CosmosExportJobRecordWrapper>( id, new PartitionKey(CosmosDbExportConstants.ExportJobPartitionKey), cancellationToken : cancellationToken); var outcome = new ExportJobOutcome(cosmosExportJobRecord.Resource.JobRecord, WeakETag.FromVersionId(cosmosExportJobRecord.Resource.ETag)); return(outcome); } catch (CosmosException dce) { if (dce.StatusCode == HttpStatusCode.TooManyRequests) { throw new RequestRateExceededException(dce.RetryAfter); } else if (dce.StatusCode == HttpStatusCode.NotFound) { throw new JobNotFoundException(string.Format(Core.Resources.JobNotFound, id)); } _logger.LogError(dce, "Failed to get an export job by id."); 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 async Task <IReadOnlyCollection <ReindexJobWrapper> > AcquireReindexJobsAsync(ushort maximumNumberOfConcurrentJobsAllowed, TimeSpan jobHeartbeatTimeoutThreshold, CancellationToken cancellationToken) { try { StoredProcedureExecuteResponse <IReadOnlyCollection <CosmosReindexJobRecordWrapper> > response = await _retryExceptionPolicyFactory.CreateRetryPolicy().ExecuteAsync( async ct => await _acquireReindexJobs.ExecuteAsync( _containerScope.Value.Scripts, maximumNumberOfConcurrentJobsAllowed, (ushort)jobHeartbeatTimeoutThreshold.TotalSeconds, ct), cancellationToken); return(response.Resource.Select(cosmosReindexWrapper => new ReindexJobWrapper(cosmosReindexWrapper.JobRecord, WeakETag.FromVersionId(cosmosReindexWrapper.ETag))).ToList()); } catch (CosmosException dce) { if (dce.GetSubStatusCode() == HttpStatusCode.RequestEntityTooLarge) { throw new RequestRateExceededException(null); } _logger.LogError(dce, "Failed to acquire reindex jobs."); throw; } }
private ExportJobOutcome CreateExportJobOutcome() { var exportRequest = new CreateExportRequest(new Uri($"http://localhost/ExportJob/"), ExportJobType.All); return(new ExportJobOutcome(new ExportJobRecord(exportRequest.RequestUri, exportRequest.RequestType, ExportFormatTags.ResourceName, null, null, "hash", rollingFileSizeInMB: 64), WeakETag.FromVersionId("0"))); }
public async Task GivenASavedResource_WhenUpsertIsAnUpdate_ThenTheExistingResourceIsUpdated() { var saveResult = await Mediator.UpsertResourceAsync(Samples.GetJsonSample("Weight")); var newResourceValues = Samples.GetJsonSample("WeightInGrams").ToPoco(); newResourceValues.Id = saveResult.Resource.Id; var updateResult = await Mediator.UpsertResourceAsync(newResourceValues.ToResourceElement(), WeakETag.FromVersionId(saveResult.Resource.VersionId)); Assert.NotNull(updateResult); Assert.Equal(SaveOutcomeType.Updated, updateResult.Outcome); var wrapper = await _fixture.DataStore.GetAsync(new ResourceKey("Observation", updateResult.Resource.Id), CancellationToken.None); Assert.NotNull(wrapper); Assert.False(wrapper.RawResource.IsMetaSet); }
public async Task GivenANonexistentResource_WhenUpsertingWithCreateDisabledAndIntegerETagHeader_TheServerShouldReturnResourceNotFoundResponse(string versionId) { SetAllowCreate(false); await Assert.ThrowsAsync <ResourceNotFoundException>(async() => await Mediator.UpsertResourceAsync(Samples.GetJsonSample("Weight"), WeakETag.FromVersionId(versionId))); }
public async Task GivenASavedResource_WhenUpserting_ThenMetaSetIsSetToFalse() { var versionId = Guid.NewGuid().ToString(); var resource = Samples.GetJsonSample("Weight").UpdateVersion(versionId); var saveResult = await Mediator.UpsertResourceAsync(resource); var newResourceValues = Samples.GetJsonSample("WeightInGrams").ToPoco(); newResourceValues.Id = saveResult.Resource.Id; var updateResult = await Mediator.UpsertResourceAsync(newResourceValues.ToResourceElement(), 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); var wrapper = await _fixture.DataStore.GetAsync(new ResourceKey("Observation", saveResult.Resource.Id), CancellationToken.None); Assert.NotNull(wrapper); Assert.False(wrapper.RawResource.IsMetaSet); Assert.NotEqual(wrapper.Version, versionId); var deserialized = _fhirJsonParser.Parse <Observation>(wrapper.RawResource.Data); Assert.Equal("1", deserialized.VersionId); }
public async Task WhenUpsertingASavedResourceWithInvalidETagHeader_GivenR4Server_ThenPreconditionFailedIsThrown() { var saveResult = await Mediator.UpsertResourceAsync(Samples.GetJsonSample("Weight")); var newResourceValues = Samples.GetJsonSample("WeightInGrams").ToPoco(); newResourceValues.Id = saveResult.Resource.Id; await Assert.ThrowsAsync <PreconditionFailedException>(async() => await Mediator.UpsertResourceAsync(newResourceValues.ToResourceElement(), WeakETag.FromVersionId("invalidVersion"))); }
public async Task GivenAnUpdatedResource_WhenUpdateSearchIndexForResourceAsync_ThenResourceGetsUpdated() { ResourceElement patientResource = Samples.GetJsonSample("Patient"); SaveOutcome upsertResult = await Mediator.UpsertResourceAsync(patientResource); (ResourceWrapper original, ResourceWrapper updated) = await CreateUpdatedWrapperFromExistingResource(upsertResult); ResourceWrapper replaceResult = await _dataStore.UpdateSearchIndexForResourceAsync(updated, WeakETag.FromVersionId(original.Version), CancellationToken.None); Assert.Equal(original.ResourceId, replaceResult.ResourceId); Assert.Equal(original.Version, replaceResult.Version); Assert.Equal(original.ResourceTypeName, replaceResult.ResourceTypeName); Assert.Equal(original.LastModified, replaceResult.LastModified); Assert.NotEqual((original as FhirCosmosResourceWrapper).ETag, (replaceResult as FhirCosmosResourceWrapper).ETag); }
public void GivenAWeakETag_WhenUsingTheWrongMethodToCreate_ThenThrow() { Assert.Throws <ArgumentException>(() => WeakETag.FromVersionId("W/\"version1\"")); }
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)); }
private ExportJobOutcome CreateExportJobOutcome(ExportJobRecord exportJobRecord, WeakETag weakETag = null) { return(new ExportJobOutcome( exportJobRecord, weakETag ?? WeakETag.FromVersionId("123"))); }
public async Task GivenAnUpdatedResourceWithWrongResourceId_WhenUpdateSearchIndexForResourceAsync_ThenExceptionIsThrown() { ResourceElement patientResource = Samples.GetJsonSample("Patient"); SaveOutcome upsertResult = await Mediator.UpsertResourceAsync(patientResource); (ResourceWrapper original, ResourceWrapper updated) = await CreateUpdatedWrapperFromExistingResource(upsertResult, Guid.NewGuid().ToString()); await Assert.ThrowsAsync <ResourceNotFoundException>(() => _dataStore.UpdateSearchIndexForResourceAsync(updated, WeakETag.FromVersionId(original.Version), CancellationToken.None)); }
public override void OnActionExecuted(ActionExecutedContext context) { EnsureArg.IsNotNull(context, nameof(context)); if (context?.Exception == null) { return; } if (context.Exception is FhirException fhirException) { var operationOutcomeResult = new OperationOutcomeResult( new OperationOutcome { Id = _fhirRequestContextAccessor.FhirRequestContext.CorrelationId, Issue = fhirException.Issues.Select(x => x.ToPoco()).ToList(), }, HttpStatusCode.BadRequest); switch (fhirException) { case UnauthorizedFhirActionException _: operationOutcomeResult.StatusCode = HttpStatusCode.Forbidden; break; case ResourceGoneException resourceGoneException: operationOutcomeResult.StatusCode = HttpStatusCode.Gone; if (!string.IsNullOrEmpty(resourceGoneException.DeletedResource?.VersionId)) { operationOutcomeResult.Headers.Add(HeaderNames.ETag, WeakETag.FromVersionId(resourceGoneException.DeletedResource.VersionId).ToString()); } break; case ResourceNotFoundException _: case JobNotFoundException _: operationOutcomeResult.StatusCode = HttpStatusCode.NotFound; break; case JobConflictException _: operationOutcomeResult.StatusCode = HttpStatusCode.Conflict; break; case MethodNotAllowedException _: operationOutcomeResult.StatusCode = HttpStatusCode.MethodNotAllowed; break; case OpenIdConfigurationException _: operationOutcomeResult.StatusCode = HttpStatusCode.ServiceUnavailable; break; case ResourceNotValidException _: if (context.ActionDescriptor is ControllerActionDescriptor controllerDescriptor) { if (controllerDescriptor.ControllerName.Equals(ValidateController, StringComparison.OrdinalIgnoreCase)) { operationOutcomeResult.StatusCode = HttpStatusCode.OK; break; } } operationOutcomeResult.StatusCode = HttpStatusCode.BadRequest; break; case BadRequestException _: case RequestNotValidException _: case BundleEntryLimitExceededException _: case ProvenanceHeaderException _: operationOutcomeResult.StatusCode = HttpStatusCode.BadRequest; break; case ResourceConflictException _: operationOutcomeResult.StatusCode = HttpStatusCode.Conflict; break; case PreconditionFailedException _: operationOutcomeResult.StatusCode = HttpStatusCode.PreconditionFailed; break; case InvalidSearchOperationException _: case SearchOperationNotSupportedException _: case CustomerManagedKeyException _: operationOutcomeResult.StatusCode = HttpStatusCode.Forbidden; break; case UnsupportedConfigurationException _: operationOutcomeResult.StatusCode = HttpStatusCode.InternalServerError; break; case OperationFailedException ofe: operationOutcomeResult.StatusCode = ofe.ResponseStatusCode; break; case OperationNotImplementedException _: operationOutcomeResult.StatusCode = HttpStatusCode.MethodNotAllowed; break; case NotAcceptableException _: operationOutcomeResult.StatusCode = HttpStatusCode.NotAcceptable; break; case RequestEntityTooLargeException _: operationOutcomeResult.StatusCode = HttpStatusCode.RequestEntityTooLarge; break; case FhirTransactionFailedException fhirTransactionFailedException: operationOutcomeResult.StatusCode = fhirTransactionFailedException.ResponseStatusCode; break; case AzureContainerRegistryTokenException azureContainerRegistryTokenException: operationOutcomeResult.StatusCode = azureContainerRegistryTokenException.StatusCode; break; case FetchTemplateCollectionFailedException _: case ConvertDataUnhandledException _: operationOutcomeResult.StatusCode = HttpStatusCode.InternalServerError; break; case ConvertDataTimeoutException _: operationOutcomeResult.StatusCode = HttpStatusCode.GatewayTimeout; break; case ConfigureCustomSearchException _: operationOutcomeResult.StatusCode = HttpStatusCode.FailedDependency; break; } context.Result = operationOutcomeResult; context.ExceptionHandled = true; } else if (context.Exception is MicrosoftHealthException microsoftHealthException) { OperationOutcomeResult healthExceptionResult; switch (microsoftHealthException) { case RequestRateExceededException ex: healthExceptionResult = CreateOperationOutcomeResult(ex.Message, OperationOutcome.IssueSeverity.Error, OperationOutcome.IssueType.Throttled, HttpStatusCode.TooManyRequests); if (ex.RetryAfter != null) { healthExceptionResult.Headers.AddRetryAfterHeaders(ex.RetryAfter.Value); } break; case UnsupportedMediaTypeException unsupportedMediaTypeException: healthExceptionResult = CreateOperationOutcomeResult(unsupportedMediaTypeException.Message, OperationOutcome.IssueSeverity.Error, OperationOutcome.IssueType.NotSupported, HttpStatusCode.UnsupportedMediaType); break; case ServiceUnavailableException serviceUnavailableException: healthExceptionResult = CreateOperationOutcomeResult(serviceUnavailableException.Message, OperationOutcome.IssueSeverity.Error, OperationOutcome.IssueType.Processing, HttpStatusCode.ServiceUnavailable); break; case TransactionFailedException transactionFailedException: healthExceptionResult = CreateOperationOutcomeResult(transactionFailedException.Message, OperationOutcome.IssueSeverity.Error, OperationOutcome.IssueType.Processing, HttpStatusCode.InternalServerError); break; case AuditException _: healthExceptionResult = CreateOperationOutcomeResult(microsoftHealthException.Message, OperationOutcome.IssueSeverity.Error, OperationOutcome.IssueType.Invalid, HttpStatusCode.BadRequest); break; case AuditHeaderCountExceededException _: case AuditHeaderTooLargeException _: healthExceptionResult = CreateOperationOutcomeResult(microsoftHealthException.Message, OperationOutcome.IssueSeverity.Error, OperationOutcome.IssueType.Invalid, HttpStatusCode.RequestHeaderFieldsTooLarge); break; default: healthExceptionResult = new OperationOutcomeResult( new OperationOutcome { Id = _fhirRequestContextAccessor.FhirRequestContext.CorrelationId, }, HttpStatusCode.InternalServerError); break; } context.Result = healthExceptionResult; context.ExceptionHandled = true; } else if (context.Exception.InnerException != null) { Exception outerException = context.Exception; context.Exception = outerException.InnerException; try { OnActionExecuted(context); } finally { if (!context.ExceptionHandled) { context.Exception = outerException; } } } }
public async Task GivenADeletedResource_WhenUpdatingSearchParameterIndexAsync_ThenExceptionIsThrown() { 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); // Update the resource wrapper, adding the new search parameter (ResourceWrapper original, ResourceWrapper updated) = await CreateUpdatedWrapperFromExistingPatient(upsertResult, searchParam, searchValue); ResourceWrapper deletedWrapper = CreateDeletedWrapper(original); await _dataStore.UpsertAsync(deletedWrapper, WeakETag.FromVersionId(deletedWrapper.Version), allowCreate : true, keepHistory : false, CancellationToken.None); // Attempt to reindex the version of the resource that hasn't been deleted await Assert.ThrowsAsync <PreconditionFailedException>(() => _dataStore.UpdateSearchParameterIndicesAsync(updated, WeakETag.FromVersionId(updated.Version), CancellationToken.None)); } finally { if (searchParam != null) { _searchParameterDefinitionManager.DeleteSearchParameter(searchParam.ToTypedElement()); await _fixture.TestHelper.DeleteSearchParameterStatusAsync(searchParam.Url, CancellationToken.None); } } }