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(); }
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 }); }