示例#1
0
        public async Task GivenATransactionBundleRequestWithNullUrl_WhenProcessing_ReturnsABadRequest()
        {
            var bundle = new Hl7.Fhir.Model.Bundle
            {
                Type  = BundleType.Transaction,
                Entry = new List <EntryComponent>
                {
                    new EntryComponent
                    {
                        Request = new RequestComponent
                        {
                            Method = HTTPVerb.PUT,
                            Url    = null,
                        },
                        Resource = new Basic {
                            Id = "test"
                        },
                    },
                },
            };

            _router.When(r => r.RouteAsync(Arg.Any <RouteContext>()))
            .Do(RouteAsyncFunction);

            var bundleRequest = new BundleRequest(bundle.ToResourceElement());

            await Assert.ThrowsAsync <RequestNotValidException>(async() => await _bundleHandler.Handle(bundleRequest, default));
        }
        public async Task <BundleResponse> Handle(BundleRequest bundleRequest, CancellationToken cancellationToken)
        {
            var bundleResource = bundleRequest.Bundle.ToPoco <Hl7.Fhir.Model.Bundle>();

            await FillRequestLists(bundleResource.Entry);

            if (bundleResource.Type == Hl7.Fhir.Model.Bundle.BundleType.Batch)
            {
                var responseBundle = new Hl7.Fhir.Model.Bundle
                {
                    Type = Hl7.Fhir.Model.Bundle.BundleType.BatchResponse,
                };

                await ExecuteAllRequests(responseBundle);

                return(new BundleResponse(responseBundle.ToResourceElement()));
            }
            else if (bundleResource.Type == Hl7.Fhir.Model.Bundle.BundleType.Transaction)
            {
                var responseBundle = new Hl7.Fhir.Model.Bundle
                {
                    Type = Hl7.Fhir.Model.Bundle.BundleType.TransactionResponse,
                };

                return(await ExecuteTransactionForAllRequests(responseBundle));
            }

            throw new MethodNotAllowedException(string.Format(Api.Resources.InvalidBundleType, bundleResource.Type));
        }
示例#3
0
        public async Task GivenABundleWithMultipleCalls_WhenProcessed_ThenANotificationWillBeEmitted(BundleType type, HTTPVerb method1, HTTPVerb method2, int code200s, int code404s)
        {
            var bundle = new Hl7.Fhir.Model.Bundle
            {
                Type  = type,
                Entry = new List <EntryComponent>
                {
                    new EntryComponent
                    {
                        Request = new RequestComponent
                        {
                            Method = method1,
                            Url    = "unused1",
                        },
                        Resource = new Patient(),
                    },
                    new EntryComponent
                    {
                        Request = new RequestComponent
                        {
                            Method = method2,
                            Url    = "unused2",
                        },
                        Resource = new Patient(),
                    },
                },
            };

            _router.When(r => r.RouteAsync(Arg.Any <RouteContext>()))
            .Do(RouteAsyncFunction);

            BundleMetricsNotification notification = null;
            await _mediator.Publish(Arg.Do <BundleMetricsNotification>(note => notification = note), Arg.Any <CancellationToken>());

            var            bundleRequest  = new BundleRequest(bundle.ToResourceElement());
            BundleResponse bundleResponse = await _bundleHandler.Handle(bundleRequest, default);

            var bundleResource = bundleResponse.Bundle.ToPoco <Hl7.Fhir.Model.Bundle>();

            Assert.Equal(type == BundleType.Batch ? BundleType.BatchResponse : BundleType.TransactionResponse, bundleResource.Type);
            Assert.Equal(2, bundleResource.Entry.Count);

            await _mediator.Received().Publish(Arg.Any <BundleMetricsNotification>(), Arg.Any <CancellationToken>());

            Assert.Equal(type == BundleType.Batch ? AuditEventSubType.Batch : AuditEventSubType.Transaction, notification.FhirOperation);

            var results = notification.ApiCallResults;

            Assert.Equal(code200s, results["200"]);

            if (code404s > 0)
            {
                Assert.Equal(code404s, results["404"]);
            }
            else
            {
                Assert.Equal(1, results.Keys.Count);
            }
        }
示例#4
0
        public async Task GivenABundle_WhenOneRequestProducesA429_429IsRetriedThenSucceeds()
        {
            var bundle = new Hl7.Fhir.Model.Bundle
            {
                Type  = BundleType.Batch,
                Entry = new List <EntryComponent>
                {
                    new EntryComponent {
                        Request = new RequestComponent {
                            Method = HTTPVerb.GET, Url = "/Patient"
                        }
                    },
                    new EntryComponent {
                        Request = new RequestComponent {
                            Method = HTTPVerb.GET, Url = "/Patient"
                        }
                    },
                    new EntryComponent {
                        Request = new RequestComponent {
                            Method = HTTPVerb.GET, Url = "/Patient"
                        }
                    },
                },
            };

            int callCount = 0;

            _router.When(r => r.RouteAsync(Arg.Any <RouteContext>()))
            .Do(info =>
            {
                info.Arg <RouteContext>().Handler = context =>
                {
                    callCount++;
                    if (callCount == 2)
                    {
                        context.Response.StatusCode = StatusCodes.Status429TooManyRequests;
                    }
                    else
                    {
                        context.Response.StatusCode = StatusCodes.Status200OK;
                    }

                    return(Task.CompletedTask);
                };
            });

            var            bundleRequest  = new BundleRequest(bundle.ToResourceElement());
            BundleResponse bundleResponse = await _bundleHandler.Handle(bundleRequest, default);

            Assert.Equal(4, callCount);
            var bundleResource = bundleResponse.Bundle.ToPoco <Hl7.Fhir.Model.Bundle>();

            Assert.Equal(3, bundleResource.Entry.Count);
            foreach (var entry in bundleResource.Entry)
            {
                Assert.Equal("200", entry.Response.Status);
            }
        }
示例#5
0
        public async Task GivenABundleWithAGet_WhenNotAuthorized_ReturnsABundleResponseWithCorrectEntry()
        {
            var bundle = new Hl7.Fhir.Model.Bundle
            {
                Type  = Hl7.Fhir.Model.Bundle.BundleType.Batch,
                Entry = new List <Hl7.Fhir.Model.Bundle.EntryComponent>
                {
                    new Hl7.Fhir.Model.Bundle.EntryComponent
                    {
                        Request = new Hl7.Fhir.Model.Bundle.RequestComponent
                        {
                            Method = Hl7.Fhir.Model.Bundle.HTTPVerb.GET,
                            Url    = "/Patient",
                        },
                    },
                },
            };

            _router.When(r => r.RouteAsync(Arg.Any <RouteContext>()))
            .Do(RouteAsyncFunction);

            var bundleRequest = new BundleRequest(bundle.ToResourceElement());

            BundleResponse bundleResponse = await _bundleHandler.Handle(bundleRequest, CancellationToken.None);

            var bundleResource = bundleResponse.Bundle.ToPoco <Hl7.Fhir.Model.Bundle>();

            Assert.Equal(Hl7.Fhir.Model.Bundle.BundleType.BatchResponse, bundleResource.Type);
            Assert.Single(bundleResource.Entry);

            Hl7.Fhir.Model.Bundle.EntryComponent entryComponent = bundleResource.Entry.First();
            Assert.Equal("403", entryComponent.Response.Status);

            var operationOutcome = entryComponent.Response.Outcome as OperationOutcome;

            Assert.NotNull(operationOutcome);
            Assert.Single(operationOutcome.Issue);

            var issueComponent = operationOutcome.Issue.First();

            Assert.Equal(OperationOutcome.IssueSeverity.Error, issueComponent.Severity);
            Assert.Equal(OperationOutcome.IssueType.Forbidden, issueComponent.Code);
            Assert.Equal("Authorization failed.", issueComponent.Diagnostics);

            void RouteAsyncFunction(CallInfo callInfo)
            {
                var routeContext = callInfo.Arg <RouteContext>();

                routeContext.Handler = context =>
                {
                    context.Response.StatusCode = 403;

                    return(Task.CompletedTask);
                };
            }
        }
示例#6
0
        public async Task GivenABundle_WhenMultipleRequests_ReturnsABundleResponseWithCorrectOrder()
        {
            var bundle = new Hl7.Fhir.Model.Bundle
            {
                Type  = Hl7.Fhir.Model.Bundle.BundleType.Batch,
                Entry = new List <Hl7.Fhir.Model.Bundle.EntryComponent>
                {
                    new Hl7.Fhir.Model.Bundle.EntryComponent
                    {
                        Request = new RequestComponent
                        {
                            Method = HTTPVerb.GET,
                            Url    = "/Patient",
                        },
                    },
                    new Hl7.Fhir.Model.Bundle.EntryComponent
                    {
                        Request = new RequestComponent
                        {
                            Method = HTTPVerb.POST,
                            Url    = "/Patient",
                        },
                        Resource = new Hl7.Fhir.Model.Patient
                        {
                        },
                    },
                    new Hl7.Fhir.Model.Bundle.EntryComponent
                    {
                        Request = new RequestComponent
                        {
                            Method = HTTPVerb.PUT,
                            Url    = "/Patient/789",
                        },
                        Resource = new Hl7.Fhir.Model.Patient
                        {
                        },
                    },
                },
            };

            _router.When(r => r.RouteAsync(Arg.Any <RouteContext>()))
            .Do(RouteAsyncFunction);

            var            bundleRequest  = new BundleRequest(bundle.ToResourceElement());
            BundleResponse bundleResponse = await _bundleHandler.Handle(bundleRequest, default);

            var bundleResource = bundleResponse.Bundle.ToPoco <Hl7.Fhir.Model.Bundle>();

            Assert.Equal(Hl7.Fhir.Model.Bundle.BundleType.BatchResponse, bundleResource.Type);
            Assert.Equal(3, bundleResource.Entry.Count);
            Assert.Equal("403", bundleResource.Entry[0].Response.Status);
            Assert.Equal("404", bundleResource.Entry[1].Response.Status);
            Assert.Equal("200", bundleResource.Entry[2].Response.Status);
        }
示例#7
0
        public async Task GivenAnEmptyBatchBundle_WhenProcessed_ReturnsABundleResponseWithNoEntries()
        {
            var bundle = new Hl7.Fhir.Model.Bundle
            {
                Type = BundleType.Batch,
            };

            var bundleRequest = new BundleRequest(bundle.ToResourceElement());

            BundleResponse bundleResponse = await _bundleHandler.Handle(bundleRequest, CancellationToken.None);

            var bundleResource = bundleResponse.Bundle.ToPoco <Hl7.Fhir.Model.Bundle>();

            Assert.Equal(BundleType.BatchResponse, bundleResource.Type);
            Assert.Empty(bundleResource.Entry);
        }
        private async Task <BundleResponse> ExecuteTransactionForAllRequests(Hl7.Fhir.Model.Bundle responseBundle)
        {
            try
            {
                using (var transaction = _transactionHandler.BeginTransaction())
                {
                    await ExecuteAllRequests(responseBundle);

                    transaction.Complete();
                }
            }
            catch (TransactionAbortedException)
            {
                _logger.LogError("Failed to commit a transaction. Throwing BadRequest as a default exception.");
                throw new TransactionFailedException(Api.Resources.GeneralTransactionFailedError, HttpStatusCode.BadRequest);
            }

            return(new BundleResponse(responseBundle.ToResourceElement()));
        }
        public async Task GivenABundle_WhenOneRequestProducesA429_SubsequentRequestAreSkipped()
        {
            var bundle = new Hl7.Fhir.Model.Bundle
            {
                Type  = BundleType.Batch,
                Entry = new List <EntryComponent>
                {
                    new EntryComponent {
                        Request = new RequestComponent {
                            Method = HTTPVerb.GET, Url = "/Patient"
                        }
                    },
                    new EntryComponent {
                        Request = new RequestComponent {
                            Method = HTTPVerb.GET, Url = "/Patient"
                        }
                    },
                },
            };

            int callCount = 0;

            _router.When(r => r.RouteAsync(Arg.Any <RouteContext>()))
            .Do(info =>
            {
                info.Arg <RouteContext>().Handler = context =>
                {
                    callCount++;
                    context.Response.StatusCode = StatusCodes.Status429TooManyRequests;
                    return(Task.CompletedTask);
                };
            });

            var            bundleRequest  = new BundleRequest(bundle.ToResourceElement());
            BundleResponse bundleResponse = await _bundleHandler.Handle(bundleRequest, default);

            Assert.Equal(1, callCount);
            var bundleResource = bundleResponse.Bundle.ToPoco <Hl7.Fhir.Model.Bundle>();

            Assert.Equal(2, bundleResource.Entry.Count);
            Assert.All(bundleResource.Entry, e => Assert.Equal("429", e.Response.Status));
        }
示例#10
0
        public async Task <BundleResponse> Handle(BundleRequest bundleRequest, CancellationToken cancellationToken)
        {
            var bundleResource = bundleRequest.Bundle.ToPoco <Hl7.Fhir.Model.Bundle>();

            if (bundleResource.Type != Hl7.Fhir.Model.Bundle.BundleType.Batch)
            {
                throw new MethodNotAllowedException(Microsoft.Health.Fhir.Api.Resources.OnlyCertainBundleTypesSupported);
            }

            await FillRequestLists(bundleResource.Entry);

            var responseBundle = new Hl7.Fhir.Model.Bundle
            {
                Type = Hl7.Fhir.Model.Bundle.BundleType.BatchResponse,
            };

            await ExecuteAllRequests(responseBundle);

            return(new BundleResponse(responseBundle.ToResourceElement()));
        }
示例#11
0
        public async Task GivenABundle_WhenProcessed_CertainResponseHeadersArePropagatedToOuterResponse()
        {
            var bundle = new Hl7.Fhir.Model.Bundle
            {
                Type  = BundleType.Batch,
                Entry = new List <EntryComponent>
                {
                    new EntryComponent {
                        Request = new RequestComponent {
                            Method = HTTPVerb.GET, Url = "/Patient"
                        }
                    },
                    new EntryComponent {
                        Request = new RequestComponent {
                            Method = HTTPVerb.GET, Url = "/Patient"
                        }
                    },
                },
            };

            string headerName = "x-ms-request-charge";

            _router.When(r => r.RouteAsync(Arg.Any <RouteContext>()))
            .Do(info =>
            {
                info.Arg <RouteContext>().Handler = context =>
                {
                    IHeaderDictionary headers = context.Response.Headers;
                    headers.TryGetValue(headerName, out StringValues existing);
                    headers[headerName] = (existing == default(StringValues) ? 2.0 : double.Parse(existing.ToString()) + 2.0).ToString(CultureInfo.InvariantCulture);
                    return(Task.CompletedTask);
                };
            });

            var bundleRequest = new BundleRequest(bundle.ToResourceElement());
            await _bundleHandler.Handle(bundleRequest, default);

            Assert.Equal("4", _fhirRequestContext.ResponseHeaders[headerName].ToString());
        }
示例#12
0
        public async Task GivenABundleWithAnExportPost_WhenProcessed_ThenItIsProcessedCorrectly()
        {
            var bundle = new Hl7.Fhir.Model.Bundle
            {
                Type  = BundleType.Batch,
                Entry = new List <EntryComponent>
                {
                    new EntryComponent {
                        Request = new RequestComponent {
                            Method = HTTPVerb.POST, Url = "/$export"
                        }
                    },
                },
            };
            var bundleRequest = new BundleRequest(bundle.ToResourceElement());

            BundleResponse bundleResponse = await _bundleHandler.Handle(bundleRequest, CancellationToken.None);

            var bundleResource = bundleResponse.Bundle.ToPoco <Hl7.Fhir.Model.Bundle>();

            Assert.Equal(BundleType.BatchResponse, bundleResource.Type);
            Assert.Single(bundleResource.Entry);
        }
示例#13
0
        public async Task GivenAFailedTransaction_WhenProcessed_ThenNoNotificationWillBeEmitted()
        {
            var bundle = new Hl7.Fhir.Model.Bundle
            {
                Type  = BundleType.Transaction,
                Entry = new List <EntryComponent>
                {
                    new EntryComponent
                    {
                        Request = new RequestComponent
                        {
                            Method = HTTPVerb.PUT,
                            Url    = "unused1",
                        },
                        Resource = new Patient(),
                    },
                    new EntryComponent
                    {
                        Request = new RequestComponent
                        {
                            // This will fail and cause an exception to be thrown
                            Method = HTTPVerb.GET,
                            Url    = "unused2",
                        },
                    },
                },
            };

            _router.When(r => r.RouteAsync(Arg.Any <RouteContext>()))
            .Do(RouteAsyncFunction);

            var bundleRequest = new BundleRequest(bundle.ToResourceElement());
            await Assert.ThrowsAsync <FhirTransactionFailedException>(() => _bundleHandler.Handle(bundleRequest, default));

            await _mediator.DidNotReceive().Publish(Arg.Any <BundleMetricsNotification>(), Arg.Any <CancellationToken>());
        }