public void Retry_success()
        {
            // Arrange the Http client
            var mockHttp = new MockHttpMessageHandler();

            // First attempt, we return HTTP 429 which means TOO MANY REQUESTS
            mockHttp.Expect(HttpMethod.Get, "https://api.fictitious-vendor.com/v1/endpoint").Respond((HttpStatusCode)429);

            // Second attempt, we return the expected result
            mockHttp.Expect(HttpMethod.Get, "https://api.fictitious-vendor.com/v1/endpoint").Respond("application/json", "{'name' : 'This is a test'}");

            var httpClient = new HttpClient(mockHttp);

            // Arrange the Request coordinator
            var coordinator = new RetryCoordinator(
                2,
                (response) => response.StatusCode == (HttpStatusCode)429,
                (attempts, response) => TimeSpan.Zero);

            // Arrange the fluent htpp client
            var fluentClient = new FluentClient("https://api.fictitious-vendor.com/v1/", httpClient)
                               .SetRequestCoordinator(coordinator);

            // Act
            var result = fluentClient
                         .GetAsync("endpoint")
                         .As <JObject>()
                         .Result;

            // Assert
            mockHttp.VerifyNoOutstandingExpectation();
            mockHttp.VerifyNoOutstandingRequest();
            Assert.That(result.Value <string>("name"), Is.EqualTo("This is a test"));
        }
        public void Retry_failure()
        {
            // Arrange the Http client
            var mockHttp = new MockHttpMessageHandler();

            // Three successive HTTP 429 (which mean TOO MANY REQUESTS)
            mockHttp.Expect(HttpMethod.Get, "https://api.fictitious-vendor.com/v1/endpoint").Respond((HttpStatusCode)429);
            mockHttp.Expect(HttpMethod.Get, "https://api.fictitious-vendor.com/v1/endpoint").Respond((HttpStatusCode)429);
            mockHttp.Expect(HttpMethod.Get, "https://api.fictitious-vendor.com/v1/endpoint").Respond((HttpStatusCode)429);

            var httpClient = new HttpClient(mockHttp);

            // Arrange the Request coordinator
            var coordinator = new RetryCoordinator(
                3,
                (response) => response.StatusCode == (HttpStatusCode)429,
                (attempts, response) => TimeSpan.Zero);

            // Arrange the fluent http client
            var fluentClient = new FluentClient("https://api.fictitious-vendor.com/v1/", httpClient)
                               .SetRequestCoordinator(coordinator);

            // Act
            Assert.ThrowsAsync <ApiException>(async() => await fluentClient.GetAsync("endpoint").As <JObject>());

            // Assert
            mockHttp.VerifyNoOutstandingExpectation();
            mockHttp.VerifyNoOutstandingRequest();
        }
Exemple #3
0
                public Logic(RetryCoordinator <TIn, TState, TOut> retry) : base(retry.Shape)
                {
                    _retry = retry;

                    SetHandler(retry.In1, onPush: () =>
                    {
                        var item = Grab(retry.In1);
                        if (!HasBeenPulled(retry.In2))
                        {
                            Pull(retry.In2);
                        }
                        Push(retry.Out2, item);
                        _elementInCycle = true;
                    }, onUpstreamFinish: () =>
                    {
                        if (!_elementInCycle)
                        {
                            CompleteStage();
                        }
                    });

                    SetHandler(retry.Out1, onPull: () =>
                    {
                        if (IsAvailable(retry.Out2))
                        {
                            Pull(retry.In1);
                        }
                        else
                        {
                            Pull(retry.In2);
                        }
                    });

                    SetHandler(retry.In2, onPush: () =>
                    {
                        _elementInCycle = false;
                        var t           = Grab(retry.In2);
                        var result      = t.Item1;

                        if (result.IsSuccess)
                        {
                            PushAndCompleteIfLast(t);
                        }
                        else
                        {
                            var r = retry._retryWith(t.Item2);
                            if (r == null)
                            {
                                PushAndCompleteIfLast(t);
                            }
                            else
                            {
                                Pull(retry.In2);
                                if (IsAvailable(retry.Out2))
                                {
                                    Push(retry.Out2, r);
                                    _elementInCycle = true;
                                }
                                else
                                {
                                    _pending = r;
                                }
                            }
                        }
                    });

                    SetHandler(retry.Out2, onPull: () =>
                    {
                        if (IsAvailable(retry.Out1) && !_elementInCycle)
                        {
                            if (_pending != null)
                            {
                                Push(retry.Out2, _pending);
                                _pending        = null;
                                _elementInCycle = true;
                            }
                            else if (!HasBeenPulled(retry.In1))
                            {
                                Pull(retry.In1);
                            }
                        }
                    }, onDownstreamFinish: () =>
                    {
                        //Do Nothing, intercept completion as downstream
                    });
                }