public async Task GivenAFhirMediator_WhenCancelingExistingExportJobEncountersJobConflictException_ThenItWillBeRetried() { _retryCount = 3; var weakETags = new WeakETag[] { WeakETag.FromVersionId("1"), WeakETag.FromVersionId("2"), WeakETag.FromVersionId("3"), }; var jobRecord = CreateExportJobRecord(OperationStatus.Queued); _fhirOperationDataStore.GetExportJobByIdAsync(JobId, _cancellationToken) .Returns( _ => CreateExportJobOutcome(CreateExportJobRecord(OperationStatus.Queued), weakETags[0]), _ => CreateExportJobOutcome(CreateExportJobRecord(OperationStatus.Queued), weakETags[1]), _ => CreateExportJobOutcome(CreateExportJobRecord(OperationStatus.Queued), weakETags[2])); SetupOperationDataStore(0, _ => throw new JobConflictException()); SetupOperationDataStore(1, _ => throw new JobConflictException()); SetupOperationDataStore(2, _ => CreateExportJobOutcome(jobRecord, WeakETag.FromVersionId("123"))); // No error should be thrown. CancelExportResponse response = await _mediator.CancelExportAsync(JobId, _cancellationToken); Assert.Equal(HttpStatusCode.Accepted, response.StatusCode); void SetupOperationDataStore(int index, Func <NSubstitute.Core.CallInfo, ExportJobOutcome> returnThis) { _fhirOperationDataStore.UpdateExportJobAsync(Arg.Any <ExportJobRecord>(), weakETags[index], Arg.Any <CancellationToken>()) .Returns(returnThis); } }
public async Task GivenAMatchingJob_WhenGettingById_ThenTheMatchingJobShouldBeReturned() { var jobRecord = await InsertNewExportJobRecordAsync(); ExportJobOutcome outcome = await _operationDataStore.GetExportJobByIdAsync(jobRecord.Id, CancellationToken.None); Assert.Equal(jobRecord.Id, outcome?.JobRecord?.Id); }
public async Task GivenAnExportJobRecord_WhenExecuted_ThenTheExportJobRecordShouldBeUpdated() { ExportJobOutcome job = await CreateAndExecuteCreateExportJobAsync(); await _exportJobTask.ExecuteAsync(job.JobRecord, job.ETag, CancellationToken.None); ExportJobOutcome actual = await _fhirOperationDataStore.GetExportJobByIdAsync(job.JobRecord.Id, CancellationToken.None); Assert.NotNull(actual); Assert.Equal(OperationStatus.Completed, actual.JobRecord.Status); }
public async Task <GetExportResponse> Handle(GetExportRequest request, CancellationToken cancellationToken) { EnsureArg.IsNotNull(request, nameof(request)); ExportJobOutcome outcome = await _fhirOperationDataStore.GetExportJobByIdAsync(request.JobId, cancellationToken); // We have an existing job. We will determine the response based on the status of the export operation. GetExportResponse exportResponse; if (outcome.JobRecord.Status.IsFinished()) { var jobResult = new ExportJobResult( outcome.JobRecord.QueuedTime, outcome.JobRecord.RequestUri, requiresAccessToken: false, outcome.JobRecord.Output.Values.OrderBy(x => x.Type, StringComparer.Ordinal).ToList(), outcome.JobRecord.Error); exportResponse = new GetExportResponse(HttpStatusCode.OK, jobResult); } else { exportResponse = new GetExportResponse(HttpStatusCode.Accepted); } return(exportResponse); }
public async Task <CancelExportResponse> Handle(CancelExportRequest request, CancellationToken cancellationToken) { EnsureArg.IsNotNull(request, nameof(request)); if (_authorizationService.CheckAccess(DataActions.Export) != DataActions.Export) { throw new UnauthorizedFhirActionException(); } return(await _retryPolicy.ExecuteAsync(async() => { ExportJobOutcome outcome = await _fhirOperationDataStore.GetExportJobByIdAsync(request.JobId, cancellationToken); // If the job is already completed for any reason, return conflict status. if (outcome.JobRecord.Status.IsFinished()) { return new CancelExportResponse(HttpStatusCode.Conflict); } // Try to cancel the job. outcome.JobRecord.Status = OperationStatus.Canceled; outcome.JobRecord.CanceledTime = Clock.UtcNow; outcome.JobRecord.FailureDetails = new ExportJobFailureDetails(Resources.UserRequestedCancellation, HttpStatusCode.NoContent); await _fhirOperationDataStore.UpdateExportJobAsync(outcome.JobRecord, outcome.ETag, cancellationToken); return new CancelExportResponse(HttpStatusCode.Accepted); })); }
public async Task <GetExportResponse> Handle(GetExportRequest request, CancellationToken cancellationToken) { EnsureArg.IsNotNull(request, nameof(request)); ExportJobOutcome outcome = await _fhirOperationDataStore.GetExportJobByIdAsync(request.JobId, cancellationToken); // We have an existing job. We will determine the response based on the status of the export operation. GetExportResponse exportResponse; if (outcome.JobRecord.Status == OperationStatus.Completed) { var jobResult = new ExportJobResult( outcome.JobRecord.QueuedTime, outcome.JobRecord.RequestUri, requiresAccessToken: false, outcome.JobRecord.Output.Values.OrderBy(x => x.Type, StringComparer.Ordinal).ToList(), outcome.JobRecord.Error); exportResponse = new GetExportResponse(HttpStatusCode.OK, jobResult); } else if (outcome.JobRecord.Status == OperationStatus.Failed || outcome.JobRecord.Status == OperationStatus.Canceled) { throw new OperationFailedException( string.Format(Resources.OperationFailed, OperationsConstants.Export, outcome.JobRecord.FailureDetails.FailureReason), outcome.JobRecord.FailureDetails.FailureStatusCode); } else { exportResponse = new GetExportResponse(HttpStatusCode.Accepted); } return(exportResponse); }
public async Task <GetExportResponse> Handle(GetExportRequest request, CancellationToken cancellationToken) { EnsureArg.IsNotNull(request, nameof(request)); if (await _authorizationService.CheckAccess(DataActions.Export) != DataActions.Export) { throw new UnauthorizedFhirActionException(); } ExportJobOutcome outcome = await _fhirOperationDataStore.GetExportJobByIdAsync(request.JobId, cancellationToken); // We have an existing job. We will determine the response based on the status of the export operation. GetExportResponse exportResponse; if (outcome.JobRecord.Status == OperationStatus.Completed) { List <ExportFileInfo> allFiles = new List <ExportFileInfo>(); foreach (List <ExportFileInfo> fileList in outcome.JobRecord.Output.Values) { allFiles.AddRange(fileList); } var jobResult = new ExportJobResult( outcome.JobRecord.QueuedTime, outcome.JobRecord.RequestUri, requiresAccessToken: false, allFiles.Select(x => x.ToExportOutputResponse()).OrderBy(x => x.Type, StringComparer.Ordinal).ToList(), outcome.JobRecord.Error.Select(x => x.ToExportOutputResponse()).ToList(), outcome.JobRecord.Issues); exportResponse = new GetExportResponse(HttpStatusCode.OK, jobResult); } else if (outcome.JobRecord.Status == OperationStatus.Failed || outcome.JobRecord.Status == OperationStatus.Canceled) { string failureReason = outcome.JobRecord.FailureDetails != null ? outcome.JobRecord.FailureDetails.FailureReason : Resources.UnknownError; HttpStatusCode failureStatusCode = outcome.JobRecord.FailureDetails != null ? outcome.JobRecord.FailureDetails.FailureStatusCode : HttpStatusCode.InternalServerError; throw new OperationFailedException( string.Format(Resources.OperationFailed, OperationsConstants.Export, failureReason), failureStatusCode); } else { exportResponse = new GetExportResponse(HttpStatusCode.Accepted); } return(exportResponse); }
public async Task <CancelExportResponse> Handle(CancelExportRequest request, CancellationToken cancellationToken) { EnsureArg.IsNotNull(request, nameof(request)); return(await _retryPolicy.ExecuteAsync(async() => { ExportJobOutcome outcome = await _fhirOperationDataStore.GetExportJobByIdAsync(request.JobId, cancellationToken); // If the job is already completed for any reason, return conflict status. if (outcome.JobRecord.Status.IsFinished()) { return new CancelExportResponse(HttpStatusCode.Conflict); } // Try to cancel the job. outcome.JobRecord.Status = OperationStatus.Canceled; outcome.JobRecord.CanceledTime = Clock.UtcNow; await _fhirOperationDataStore.UpdateExportJobAsync(outcome.JobRecord, outcome.ETag, cancellationToken); return new CancelExportResponse(HttpStatusCode.Accepted); })); }