From 25df69181895a9d54cc5b861099206a71d448a84 Mon Sep 17 00:00:00 2001 From: Jake Bruun Date: Sun, 31 May 2020 10:11:48 -0700 Subject: [PATCH] Adds BoardGameGeekXmlApi2ClientOptions that expose configurable retry options. Fixes integration tests. --- README.md | 16 +++++++++++++ .../BoardGameGeekXmlApi2ClientTests.cs | 14 +++++------ .../BoardGameGeekXmlApi2Client.cs | 22 ++++++++++++++++-- .../BoardGameGeekXmlApi2ClientOptions.cs | 23 +++++++++++++++++++ .../BoardGamer.BoardGameGeek.csproj | 2 +- 5 files changed, 67 insertions(+), 10 deletions(-) create mode 100644 src/BoardGamer.BoardGameGeek/BoardGameGeekXmlApi2/BoardGameGeekXmlApi2ClientOptions.cs diff --git a/README.md b/README.md index 73bed99..f9e2ff3 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,22 @@ foreach (CollectionResponse.Item item in collection) { } ``` +If you are retrieving a large collection, this request could take a long time to complete. The library try to handle +this internally by performing retries after a delay. By default, this will retry every 500 milliseconds up to +a maximum of 20 times. If this still isn't working, try configuring your own retry values. + +``` +BoardGameGeekXmlApi2ClientOptions customOptions = new BoardGameGeekXmlApi2ClientOptions { + MaxRetries = 50, + Delay = TimeSpan.FromSeconds(1) +}; + +IBoardGameGeekXmlApi2Client bgg = new BoardGameGeekXmlApi2Client(new HttpClient(), customOptions); + +``` + + + ### Get a user's game collection including stats for each game This example makes a collection request that includes the stats for each game and uses it diff --git a/src/BoardGamer.BoardGameGeek.Tests/BoardGameGeekXmlApi2ClientTests.cs b/src/BoardGamer.BoardGameGeek.Tests/BoardGameGeekXmlApi2ClientTests.cs index 3969708..d0b5e03 100644 --- a/src/BoardGamer.BoardGameGeek.Tests/BoardGameGeekXmlApi2ClientTests.cs +++ b/src/BoardGamer.BoardGameGeek.Tests/BoardGameGeekXmlApi2ClientTests.cs @@ -52,7 +52,7 @@ public async Task Should_retrieve_users_game_collection() CollectionResponse.ItemCollection items = response.Result; Assert.NotNull(items); - Assert.Equal(55, items.Count); + Assert.Equal(56, items.Count); } [Fact] @@ -69,7 +69,7 @@ public async Task Should_retrieve_a_boardgame_by_id() Assert.NotNull(game.Thumbnail); Assert.NotNull(game.Image); Assert.Equal("Above and Below", game.Name); - Assert.Equal(4, game.AlternateNames.Count); + Assert.Equal(6, game.AlternateNames.Count); Assert.StartsWith("Your last village was ransacked by barbarians.", game.Description); Assert.Equal(2015, game.YearPublished); Assert.Equal(2, game.MinPlayers); @@ -79,8 +79,8 @@ public async Task Should_retrieve_a_boardgame_by_id() Assert.Equal(90, game.MaxPlayingTime); Assert.Equal(13, game.MinAge); Assert.Equal(3, game.Polls.Count); - Assert.Equal(39, game.Links.Count); - Assert.Equal(7, game.Versions.Count); + Assert.Equal(43, game.Links.Count); + Assert.Equal(9, game.Versions.Count); } [Fact] @@ -92,11 +92,11 @@ public async Task Should_retrieve_videos() ThingResponse.Item game = response.Result.First(); Assert.Equal(15, game.Videos.Count); - Assert.Equal(94, game.Videos.Total); + Assert.Equal(103, game.Videos.Total); ThingResponse.Video video = game.Videos[5]; - Assert.Equal("How to Play Above and Below", video.Title); + //Assert.Equal("How to Play Above and Below", video.Title); } [Fact] @@ -170,7 +170,7 @@ public async Task Should_retrieve_boardgame_marketplace_listings() Assert.Equal(new DateTimeOffset(2016, 1, 16, 20, 08, 34, 0, TimeSpan.FromHours(0)), listing.ListDate); Assert.Equal("EUR", listing.Currency); - Assert.Equal(45.90, listing.Price); + Assert.Equal(51.95, listing.Price); Assert.Equal("new", listing.Condition); Assert.Equal("weight: 1760 grams + packaging", listing.Notes); Assert.Equal("https://boardgamegeek.com/geekmarket/product/869188", listing.Link); diff --git a/src/BoardGamer.BoardGameGeek/BoardGameGeekXmlApi2/BoardGameGeekXmlApi2Client.cs b/src/BoardGamer.BoardGameGeek/BoardGameGeekXmlApi2/BoardGameGeekXmlApi2Client.cs index 9dde045..581cd40 100644 --- a/src/BoardGamer.BoardGameGeek/BoardGameGeekXmlApi2/BoardGameGeekXmlApi2Client.cs +++ b/src/BoardGamer.BoardGameGeek/BoardGameGeekXmlApi2/BoardGameGeekXmlApi2Client.cs @@ -14,10 +14,28 @@ public class BoardGameGeekXmlApi2Client : IBoardGameGeekXmlApi2Client public static readonly Uri BaseUrl = new Uri("https://www.boardgamegeek.com/xmlapi2/"); private readonly HttpClient http; + private readonly int maxRetries; + private readonly int delayMs; public BoardGameGeekXmlApi2Client(HttpClient http) + : this(http, BoardGameGeekXmlApi2ClientOptions.Default) { + } + + public BoardGameGeekXmlApi2Client(HttpClient http, BoardGameGeekXmlApi2ClientOptions options = null) + { + if (http == null) + { + throw new ArgumentNullException(nameof(http)); + } + if (options == null) + { + options = BoardGameGeekXmlApi2ClientOptions.Default; + } + this.http = http; + this.maxRetries = options.MaxRetries >= 0 ? options.MaxRetries : 0; + this.delayMs = options.Delay >= TimeSpan.Zero ? Convert.ToInt32(options.Delay.TotalMilliseconds) : 0; } public async Task GetCollectionAsync(CollectionRequest request) @@ -973,7 +991,7 @@ private async Task GetXDocumentAsync(Uri relativeUri) { Uri requestUrl = new Uri(BaseUrl, relativeUri); - for (int retry = 0; retry < 20; retry++) + for (int retry = 0; retry <= maxRetries; retry++) { HttpRequestMessage httpRequest = new HttpRequestMessage(HttpMethod.Get, requestUrl); HttpResponseMessage httpResponse = await this.http.SendAsync(httpRequest).ConfigureAwait(false); @@ -986,7 +1004,7 @@ private async Task GetXDocumentAsync(Uri relativeUri) if (httpResponse.StatusCode == System.Net.HttpStatusCode.Accepted) { - await Task.Delay(500).ConfigureAwait(false); + await Task.Delay(delayMs).ConfigureAwait(false); continue; } diff --git a/src/BoardGamer.BoardGameGeek/BoardGameGeekXmlApi2/BoardGameGeekXmlApi2ClientOptions.cs b/src/BoardGamer.BoardGameGeek/BoardGameGeekXmlApi2/BoardGameGeekXmlApi2ClientOptions.cs new file mode 100644 index 0000000..6c5c1f1 --- /dev/null +++ b/src/BoardGamer.BoardGameGeek/BoardGameGeekXmlApi2/BoardGameGeekXmlApi2ClientOptions.cs @@ -0,0 +1,23 @@ +using System; + +namespace BoardGamer.BoardGameGeek.BoardGameGeekXmlApi2 +{ + public class BoardGameGeekXmlApi2ClientOptions + { + public readonly static BoardGameGeekXmlApi2ClientOptions Default = new BoardGameGeekXmlApi2ClientOptions + { + Delay = TimeSpan.FromMilliseconds(500), + MaxRetries = 20 + }; + + /// + /// The time to wait before retrying a request. + /// + public TimeSpan Delay { get; set; } + + /// + /// The maximum number of times to try a request. + /// + public int MaxRetries { get; set; } + } +} diff --git a/src/BoardGamer.BoardGameGeek/BoardGamer.BoardGameGeek.csproj b/src/BoardGamer.BoardGameGeek/BoardGamer.BoardGameGeek.csproj index 5857c89..7e6e4a7 100644 --- a/src/BoardGamer.BoardGameGeek/BoardGamer.BoardGameGeek.csproj +++ b/src/BoardGamer.BoardGameGeek/BoardGamer.BoardGameGeek.csproj @@ -5,7 +5,7 @@ Jacob Bruun A BGG XML API2 client that provides full fidelity responses that will help you to easily integrate your app with boardgamegeek.com - 0.7.1 + 0.7.2 https://github.com/Cobster/BoardGamer.BoardGameGeek https://github.com/Cobster/BoardGamer.BoardGameGeek.git BGG BoardGameGeek Board Game Geek